HTTP request for a single URL or list of URLs based on arguments provided

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












I have the following code that includes a simple class AioRequest. I would like to be able to call an instance of this class directly with a single URL or a list of URLs and return the results.



request = AioRequest()
single_response = request('get', 'http://httpbin.org/get')


or



url_list = ['http://httpbin.org/get', 'http://google/some_json']
list_of_responses = request('get', url_list)


I'm wondering what the best practice is for two strategies I am using:



  1. Determining if the argument is a list of URLs or a string with a single URL. Should it be two separate arguments like __call__ or should I type check like __call__2?


  2. I would like to use the __call__ method as the main API for the AioRequest instance, but is returning two different types based on the arguments given a good idea? Is is safe to rely on the user to determine the return type (example: give a single string get a single response, give a list of strings, get a list of responses)?




import asyncio
from aiohttp import ClientSession
from collections import namedtuple


Response = namedtuple(
"Response",
["url", "status_code", "json"]
)


class AioRequest:

def __init__(self, session=None):
self._loop = asyncio.get_event_loop()
self._session = session or ClientSession(loop=self._loop)

def __call__(self, method, url=None, url_list=None, **kwargs):
urls = url_list or
urls.append(url)
requests = [self._request(method, url, **kwargs)
for url in urls
if url is not None]
return self._run(*requests)

def __call__2(self, method, url_or_list_of, **kwargs):
if type(url_or_list_of) is str:
return self._run(method, url_or_list_of, **kwargs)
else:
requests = [self._request(method, url, **kwargs)
for url in url_or_list_of]
return self._run(*requests)

async def _request(self, method, url, **kwargs):
response = await self._session.request(method, url, **kwargs)
return Response(url, response.status, await response.json())

def _run(self, *tasks):
task_group = asyncio.gather(*tasks)
results = self._loop.run_until_complete(task_group) # results = list of responses
if len(results) == 1:
return results[0]
return results

def __del__(self):
self._session.close()






share|improve this question



























    up vote
    3
    down vote

    favorite












    I have the following code that includes a simple class AioRequest. I would like to be able to call an instance of this class directly with a single URL or a list of URLs and return the results.



    request = AioRequest()
    single_response = request('get', 'http://httpbin.org/get')


    or



    url_list = ['http://httpbin.org/get', 'http://google/some_json']
    list_of_responses = request('get', url_list)


    I'm wondering what the best practice is for two strategies I am using:



    1. Determining if the argument is a list of URLs or a string with a single URL. Should it be two separate arguments like __call__ or should I type check like __call__2?


    2. I would like to use the __call__ method as the main API for the AioRequest instance, but is returning two different types based on the arguments given a good idea? Is is safe to rely on the user to determine the return type (example: give a single string get a single response, give a list of strings, get a list of responses)?




    import asyncio
    from aiohttp import ClientSession
    from collections import namedtuple


    Response = namedtuple(
    "Response",
    ["url", "status_code", "json"]
    )


    class AioRequest:

    def __init__(self, session=None):
    self._loop = asyncio.get_event_loop()
    self._session = session or ClientSession(loop=self._loop)

    def __call__(self, method, url=None, url_list=None, **kwargs):
    urls = url_list or
    urls.append(url)
    requests = [self._request(method, url, **kwargs)
    for url in urls
    if url is not None]
    return self._run(*requests)

    def __call__2(self, method, url_or_list_of, **kwargs):
    if type(url_or_list_of) is str:
    return self._run(method, url_or_list_of, **kwargs)
    else:
    requests = [self._request(method, url, **kwargs)
    for url in url_or_list_of]
    return self._run(*requests)

    async def _request(self, method, url, **kwargs):
    response = await self._session.request(method, url, **kwargs)
    return Response(url, response.status, await response.json())

    def _run(self, *tasks):
    task_group = asyncio.gather(*tasks)
    results = self._loop.run_until_complete(task_group) # results = list of responses
    if len(results) == 1:
    return results[0]
    return results

    def __del__(self):
    self._session.close()






    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      I have the following code that includes a simple class AioRequest. I would like to be able to call an instance of this class directly with a single URL or a list of URLs and return the results.



      request = AioRequest()
      single_response = request('get', 'http://httpbin.org/get')


      or



      url_list = ['http://httpbin.org/get', 'http://google/some_json']
      list_of_responses = request('get', url_list)


      I'm wondering what the best practice is for two strategies I am using:



      1. Determining if the argument is a list of URLs or a string with a single URL. Should it be two separate arguments like __call__ or should I type check like __call__2?


      2. I would like to use the __call__ method as the main API for the AioRequest instance, but is returning two different types based on the arguments given a good idea? Is is safe to rely on the user to determine the return type (example: give a single string get a single response, give a list of strings, get a list of responses)?




      import asyncio
      from aiohttp import ClientSession
      from collections import namedtuple


      Response = namedtuple(
      "Response",
      ["url", "status_code", "json"]
      )


      class AioRequest:

      def __init__(self, session=None):
      self._loop = asyncio.get_event_loop()
      self._session = session or ClientSession(loop=self._loop)

      def __call__(self, method, url=None, url_list=None, **kwargs):
      urls = url_list or
      urls.append(url)
      requests = [self._request(method, url, **kwargs)
      for url in urls
      if url is not None]
      return self._run(*requests)

      def __call__2(self, method, url_or_list_of, **kwargs):
      if type(url_or_list_of) is str:
      return self._run(method, url_or_list_of, **kwargs)
      else:
      requests = [self._request(method, url, **kwargs)
      for url in url_or_list_of]
      return self._run(*requests)

      async def _request(self, method, url, **kwargs):
      response = await self._session.request(method, url, **kwargs)
      return Response(url, response.status, await response.json())

      def _run(self, *tasks):
      task_group = asyncio.gather(*tasks)
      results = self._loop.run_until_complete(task_group) # results = list of responses
      if len(results) == 1:
      return results[0]
      return results

      def __del__(self):
      self._session.close()






      share|improve this question













      I have the following code that includes a simple class AioRequest. I would like to be able to call an instance of this class directly with a single URL or a list of URLs and return the results.



      request = AioRequest()
      single_response = request('get', 'http://httpbin.org/get')


      or



      url_list = ['http://httpbin.org/get', 'http://google/some_json']
      list_of_responses = request('get', url_list)


      I'm wondering what the best practice is for two strategies I am using:



      1. Determining if the argument is a list of URLs or a string with a single URL. Should it be two separate arguments like __call__ or should I type check like __call__2?


      2. I would like to use the __call__ method as the main API for the AioRequest instance, but is returning two different types based on the arguments given a good idea? Is is safe to rely on the user to determine the return type (example: give a single string get a single response, give a list of strings, get a list of responses)?




      import asyncio
      from aiohttp import ClientSession
      from collections import namedtuple


      Response = namedtuple(
      "Response",
      ["url", "status_code", "json"]
      )


      class AioRequest:

      def __init__(self, session=None):
      self._loop = asyncio.get_event_loop()
      self._session = session or ClientSession(loop=self._loop)

      def __call__(self, method, url=None, url_list=None, **kwargs):
      urls = url_list or
      urls.append(url)
      requests = [self._request(method, url, **kwargs)
      for url in urls
      if url is not None]
      return self._run(*requests)

      def __call__2(self, method, url_or_list_of, **kwargs):
      if type(url_or_list_of) is str:
      return self._run(method, url_or_list_of, **kwargs)
      else:
      requests = [self._request(method, url, **kwargs)
      for url in url_or_list_of]
      return self._run(*requests)

      async def _request(self, method, url, **kwargs):
      response = await self._session.request(method, url, **kwargs)
      return Response(url, response.status, await response.json())

      def _run(self, *tasks):
      task_group = asyncio.gather(*tasks)
      results = self._loop.run_until_complete(task_group) # results = list of responses
      if len(results) == 1:
      return results[0]
      return results

      def __del__(self):
      self._session.close()








      share|improve this question












      share|improve this question




      share|improve this question








      edited Jan 13 at 1:15









      Jamal♦

      30.1k11114225




      30.1k11114225









      asked Jan 13 at 0:12









      JohnBoy

      383




      383




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          __call__ really confuses me. Type checking is the way to go for readability. On top of that, if you need to accept new types later, all you need to do is add an if statement.



          In my opinion, if you absolutely want the brevity of __call__, accepting multiple types of arguments is fine, and so is returning multiple types (as is the case with many builtin and standard library functions). It is important that you mention this in the documentation, though.




          Some other questionable things:




          • Instead of comparing type directly, you can use isinstance:



            if isinstance(url_or_list_of, str):
            ...


          • url_or_list_of is a somewhat unfriendly name for a public API. How about url_or_list_of_urls?



          • It's good that you provide a 'clean exit' method. __del__ isn't the right method to override, however, since the implementation is CPython-specific, and it may never actually be called. Instead, try defining __enter__ and __exit__:



            class AioRequest:
            ...
            def __enter__(self):
            return self
            # This should be all in most cases,
            # unless you want to do something with `self` before returning
            ...
            def __exit__(self, exc_type, exc_value, exc_traceback):
            self._session.close()


            You can then use with:



             with AioRequest() as request:
            # __enter__ called
            response = request( ... )
            # __exit__ called






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185000%2fhttp-request-for-a-single-url-or-list-of-urls-based-on-arguments-provided%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote













            __call__ really confuses me. Type checking is the way to go for readability. On top of that, if you need to accept new types later, all you need to do is add an if statement.



            In my opinion, if you absolutely want the brevity of __call__, accepting multiple types of arguments is fine, and so is returning multiple types (as is the case with many builtin and standard library functions). It is important that you mention this in the documentation, though.




            Some other questionable things:




            • Instead of comparing type directly, you can use isinstance:



              if isinstance(url_or_list_of, str):
              ...


            • url_or_list_of is a somewhat unfriendly name for a public API. How about url_or_list_of_urls?



            • It's good that you provide a 'clean exit' method. __del__ isn't the right method to override, however, since the implementation is CPython-specific, and it may never actually be called. Instead, try defining __enter__ and __exit__:



              class AioRequest:
              ...
              def __enter__(self):
              return self
              # This should be all in most cases,
              # unless you want to do something with `self` before returning
              ...
              def __exit__(self, exc_type, exc_value, exc_traceback):
              self._session.close()


              You can then use with:



               with AioRequest() as request:
              # __enter__ called
              response = request( ... )
              # __exit__ called






            share|improve this answer

























              up vote
              1
              down vote













              __call__ really confuses me. Type checking is the way to go for readability. On top of that, if you need to accept new types later, all you need to do is add an if statement.



              In my opinion, if you absolutely want the brevity of __call__, accepting multiple types of arguments is fine, and so is returning multiple types (as is the case with many builtin and standard library functions). It is important that you mention this in the documentation, though.




              Some other questionable things:




              • Instead of comparing type directly, you can use isinstance:



                if isinstance(url_or_list_of, str):
                ...


              • url_or_list_of is a somewhat unfriendly name for a public API. How about url_or_list_of_urls?



              • It's good that you provide a 'clean exit' method. __del__ isn't the right method to override, however, since the implementation is CPython-specific, and it may never actually be called. Instead, try defining __enter__ and __exit__:



                class AioRequest:
                ...
                def __enter__(self):
                return self
                # This should be all in most cases,
                # unless you want to do something with `self` before returning
                ...
                def __exit__(self, exc_type, exc_value, exc_traceback):
                self._session.close()


                You can then use with:



                 with AioRequest() as request:
                # __enter__ called
                response = request( ... )
                # __exit__ called






              share|improve this answer























                up vote
                1
                down vote










                up vote
                1
                down vote









                __call__ really confuses me. Type checking is the way to go for readability. On top of that, if you need to accept new types later, all you need to do is add an if statement.



                In my opinion, if you absolutely want the brevity of __call__, accepting multiple types of arguments is fine, and so is returning multiple types (as is the case with many builtin and standard library functions). It is important that you mention this in the documentation, though.




                Some other questionable things:




                • Instead of comparing type directly, you can use isinstance:



                  if isinstance(url_or_list_of, str):
                  ...


                • url_or_list_of is a somewhat unfriendly name for a public API. How about url_or_list_of_urls?



                • It's good that you provide a 'clean exit' method. __del__ isn't the right method to override, however, since the implementation is CPython-specific, and it may never actually be called. Instead, try defining __enter__ and __exit__:



                  class AioRequest:
                  ...
                  def __enter__(self):
                  return self
                  # This should be all in most cases,
                  # unless you want to do something with `self` before returning
                  ...
                  def __exit__(self, exc_type, exc_value, exc_traceback):
                  self._session.close()


                  You can then use with:



                   with AioRequest() as request:
                  # __enter__ called
                  response = request( ... )
                  # __exit__ called






                share|improve this answer













                __call__ really confuses me. Type checking is the way to go for readability. On top of that, if you need to accept new types later, all you need to do is add an if statement.



                In my opinion, if you absolutely want the brevity of __call__, accepting multiple types of arguments is fine, and so is returning multiple types (as is the case with many builtin and standard library functions). It is important that you mention this in the documentation, though.




                Some other questionable things:




                • Instead of comparing type directly, you can use isinstance:



                  if isinstance(url_or_list_of, str):
                  ...


                • url_or_list_of is a somewhat unfriendly name for a public API. How about url_or_list_of_urls?



                • It's good that you provide a 'clean exit' method. __del__ isn't the right method to override, however, since the implementation is CPython-specific, and it may never actually be called. Instead, try defining __enter__ and __exit__:



                  class AioRequest:
                  ...
                  def __enter__(self):
                  return self
                  # This should be all in most cases,
                  # unless you want to do something with `self` before returning
                  ...
                  def __exit__(self, exc_type, exc_value, exc_traceback):
                  self._session.close()


                  You can then use with:



                   with AioRequest() as request:
                  # __enter__ called
                  response = request( ... )
                  # __exit__ called







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Jan 13 at 9:52









                Daniel

                4,1132836




                4,1132836






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185000%2fhttp-request-for-a-single-url-or-list-of-urls-based-on-arguments-provided%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Chat program with C++ and SFML

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    Will my employers contract hold up in court?