Skip to content

Commit

Permalink
pokedex#7: "makes pokemon requests to downstream api concurrent" (#15)
Browse files Browse the repository at this point in the history
# Summary:

Bottleneck assuaged as `pokedex.api.client.get_pokemon` is now powered by [actionpack](https://github.com/withtwoemms/actionpack). The following call, which used to routinely take over 20 seconds, now runs in ~6 seconds now:
``` 
poetry run get-pokemon by --type ghost
```

### Notes:
* in the future, will consider means of increasing batch size when requesting from upstream API
* closes #7
  • Loading branch information
withtwoemms committed May 30, 2023
1 parent 48e5917 commit 97b701d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 13 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -25,5 +25,7 @@ There exists a `poetry` buildscript called `get-pokemon`.
It can be invoked like so:

```
poetry run get-pokemon
poetry run get-pokemon by --type ghost
```

pipe to `less`
81 changes: 79 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions pokedex/api/__main__.py
Expand Up @@ -23,12 +23,8 @@ def go(args=sys.argv):
args = parser.parse_args()

if args.type:
for pokemon in get_pokemon_by_type(args.type):
output = json.dumps(pokemon, indent=4)
print(output)
print(json.dumps(list(get_pokemon_by_type(args.type)), indent=4))

if args.move:
move = str(args.move).replace(" ", "-")
for pokemon in get_pokemon_by_move(move):
output = json.dumps(pokemon, indent=4)
print(output)
print(json.dumps(list(get_pokemon_by_move(move)), indent=4))
13 changes: 9 additions & 4 deletions pokedex/api/client.py
@@ -1,7 +1,11 @@
from typing import Any, Dict, Generator, Iterable, List, Optional
from actionpack import Procedure

import requests

from actionpack.actions import Call
from actionpack.utils import Closure

from pokedex.api.constants import BASE_URL
from pokedex.api.models import PokeApiRequest, PokeApiResource, PokeApiResourceRef, PokemonRef

Expand Down Expand Up @@ -38,11 +42,12 @@ def generate_pokemon_requests(api_request: PokeApiRequest, response_key: str) ->
yield model.as_request()


# TODO: make concurrent
def get_pokemon(pokemon_requests: Iterable[PokeApiRequest]) -> Generator[Pokemon, None, None]:
for pokemon_request in pokemon_requests:
response: requests.Response = pokemon_request()
response.raise_for_status() # TODO: handle error states
calls = (Call(Closure(pokemon_request)) for pokemon_request in pokemon_requests)
for result in Procedure(calls).execute(synchronously=False, should_raise=True):
# TODO: consider how to proceed if `result.successful => False`
# such handling will preclude the need for the `should_raise` option during Procedure execution
response: requests.Response = result.value
yield response.json()


Expand Down
4 changes: 4 additions & 0 deletions pokedex/api/models.py
Expand Up @@ -14,6 +14,10 @@ class PokeApiRequest:
def __call__(self) -> ApiResponseType:
return requests.get(self.url)

@property
def __name__(self):
return f"{self.__class__.__name__}:{self.url}"


class PokeApiResourceRef(BaseModel):
name: str
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Expand Up @@ -9,6 +9,7 @@ python = "^3.8.1"
requests = "2.31.0"
urllib3 = "<=1.26.16"
pydantic = "^1.10.8"
actionpack = "^1.7.15"

[tool.poetry.group.dev.dependencies]
coverage = "5.5"
Expand Down

0 comments on commit 97b701d

Please sign in to comment.