Skip to content

Commit 6bb891c

Browse files
committed
Initial commit
1 parent 517929c commit 6bb891c

15 files changed

+602
-0
lines changed

LICENSE renamed to LICENSE.txt

File renamed without changes.

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include README.rst
2+
recursive-include pyroar/test *
3+
recursive-include pyroar/resources *

README.rst

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
PyRoar
2+
======
3+
4+
- This Python package aims to provide easy acces to the data stored in the `Pokéapi`_ RESTful service.
5+
6+
- This project was conceived as a way to learn how to create classes and methods dynamically. Therefore, the RESTful client classes and their methods to retrieve data are not explicitly defined. The Pokéapi service is asked for the resources it provides and classes and methods are created in real time. This allows adaptability to the service but can can cause some problems if it goes through major changes.
7+
8+
Pokéapi - The RESTful Pokémon API
9+
---------------------------------
10+
11+
- `Pokéapi`_ (Copyright Paul Hallett) is a RESTful API interface to highly detailed data related to the `Pokémon`_ video game franchise.
12+
13+
- Using this RESTful service you can consume information on Pokémon, their moves, abilities, types, egg groups, etc.
14+
15+
- For more information, please read the `Pokéapi V2 Documentation`_.
16+
17+
18+
Installation
19+
------------
20+
21+
Cloning
22+
```````
23+
PyRoar can be cloned in your local machine by executing in your terminal::
24+
25+
$ git clone https://github.com/dapregi/pyroar.git
26+
27+
Once you have downloaded the project you can install the library::
28+
29+
$ cd pyroar
30+
$ python setup.py install
31+
32+
Usage
33+
-----
34+
35+
Getting started
36+
```````````````
37+
The first step is to import the module and initialize the PokeClient:
38+
39+
.. code-block:: python
40+
41+
>>> from pyroar.pokeclient import PokeClient
42+
>>> pc = PokeClient()
43+
44+
The second step is to create the specific client for the data we want to query:
45+
46+
.. code-block:: python
47+
48+
>>> pokemon = pc.get_pokemon()
49+
>>> berry = pc.get_berry()
50+
>>> machine = pc.get_machine()
51+
52+
And now, you can start asking to the Pokéapi service by providing a query ID:
53+
54+
.. code-block:: python
55+
56+
>>> pokemon.get_height('bulbasaur')
57+
7
58+
59+
>>> pokemon.get_types('bulbasaur')
60+
[{u'slot': 2, u'type': {u'name': u'poison', u'url': u'http://pokeapi.co/api/v2/type/4/'}}, {u'slot': 1, u'type': {u'name': u'grass', u'url': u'http://pokeapi.co/api/v2/type/12/'}}]
61+
62+
>>> berry.get_growth_time('cheri')
63+
3
64+
65+
>>> machine.get_move('1')
66+
{u'name': u'mega-punch', u'url': u'http://pokeapi.co/api/v2/move/5/'}
67+
68+
Some data can be accessed specifying either id or name:
69+
70+
.. code-block:: python
71+
72+
>>> pokemon.get_weight('bulbasaur')
73+
69
74+
>>> pokemon.get_weight('1')
75+
69
76+
77+
Results are retrieved as JSON. Therefore, they can be queried by key:
78+
79+
.. code-block:: python
80+
81+
>>> for type in pokemon.get_types('bulbasaur'):
82+
... print type['type']['name']
83+
poison
84+
grass
85+
86+
>>> machine.get_move('1')['name']
87+
mega-punch
88+
89+
To retrieve all the information for a resource just use the method "get()"
90+
91+
.. code-block:: python
92+
93+
>>> pc.get('machine', '1')
94+
{u'item': {u'url': u'http://pokeapi.co/api/v2/item/305/', u'name': u'tm01'}, u'move': {u'url': u'http://pokeapi.co/api/v2/move/5/', u'name': u'mega-punch'}, u'id': 1, u'version_group': {u'url': u'http://pokeapi.co/api/v2/version-group/1/', u'name': u'red-blue'}}
95+
96+
>>> machine.get('1')
97+
{u'item': {u'url': u'http://pokeapi.co/api/v2/item/305/', u'name': u'tm01'}, u'move': {u'url': u'http://pokeapi.co/api/v2/move/5/', u'name': u'mega-punch'}, u'id': 1, u'version_group': {u'url': u'http://pokeapi.co/api/v2/version-group/1/', u'name': u'red-blue'}}
98+
99+
What can I ask for?
100+
```````````````````
101+
As client classes and client methods are dynamically created, the best way to know the methods of an object is either checking out the `Pokéapi V2 Documentation`_ or using the built-in method "get_methods()":
102+
103+
.. code-block:: python
104+
105+
>>> pc.get_methods()
106+
['get', 'get_ability', 'get_berry', 'get_berry_firmness',
107+
'get_berry_flavor', 'get_characteristic', 'get_config',
108+
'get_contest_effect', 'get_contest_type', 'get_egg_group',
109+
'get_encounter_condition', 'get_encounter_condition_value',
110+
'get_encounter_method', 'get_evolution_chain', 'get_evolution_trigger',
111+
'get_gender', 'get_generation', 'get_growth_rate', 'get_item',
112+
'get_item_attribute', 'get_item_category', 'get_item_fling_effect',
113+
'get_item_pocket', 'get_language', 'get_location', 'get_location_area',
114+
'get_machine', 'get_methods', 'get_move', 'get_move_ailment',
115+
'get_move_battle_style', 'get_move_category', 'get_move_damage_class',
116+
'get_move_learn_method', 'get_move_target', 'get_nature',
117+
'get_pal_park_area', 'get_pokeathlon_stat', 'get_pokedex', 'get_pokemon',
118+
'get_pokemon_color', 'get_pokemon_form', 'get_pokemon_habitat',
119+
'get_pokemon_shape', 'get_pokemon_species', 'get_region', 'get_stat',
120+
'get_super_contest_effect', 'get_type', 'get_version', 'get_version_group']
121+
122+
>>> pokemon = pc.get_pokemon()
123+
>>> pokemon.get_methods()
124+
['get', 'get_abilities', 'get_base_experience', 'get_forms',
125+
'get_game_indices', 'get_height', 'get_held_items', 'get_id',
126+
'get_is_default', 'get_location_area_encounters', 'get_methods',
127+
'get_moves', 'get_name', 'get_order', 'get_species', 'get_sprites',
128+
'get_stats', 'get_types', 'get_weight']
129+
130+
131+
Configuration
132+
`````````````
133+
134+
Configuration stores the REST services host and the API version.
135+
136+
Default configuration:
137+
138+
.. code-block:: python
139+
140+
>>> pc.get_config()
141+
{'host': 'pokeapi.co', 'version': 'v2'}
142+
143+
A custom configuration can be passed to PokeClient with a ConfigClient object. JSON and YML files are supported:
144+
145+
.. code-block:: python
146+
147+
>>> cc = ConfigClient('config.json')
148+
>>> pc = PokeClient(cc)
149+
150+
If you want to change the configuration you can directly modify the ConfigClient object:
151+
152+
.. code-block:: python
153+
154+
>>> cc = ConfigClient()
155+
>>> pc = PokeClient(cc)
156+
>>> pc.get_config()
157+
{'host': 'pokeapi.co', 'version': 'v2'}
158+
>>> cc.version = 'v3'
159+
>>> pc.get_config()
160+
{'host': 'pokeapi.co', 'version': 'v3'}
161+
162+
WARNING
163+
```````
164+
From `Pokéapi V2 Documentation`_:
165+
166+
- This is a **consumption-only** API - only the HTTP GET method is available on resources. No authentication is required to access this API. All resources are fully open and available.
167+
168+
- **No authentication** is required to access this API. All resources are fully open and available.
169+
170+
- There is, however, a daily rate limit of 300 requests **per resource** per IP address. So a single IP address can call the bulbasaur resource 300 times a day. Not 300 requests across the entire dataset! This is to stop our database from falling over under heavy load.
171+
172+
- If you are going to be regularly using the API, I recommend caching data on your service.
173+
174+
- Luckily, we provide **modified/created datetime stamps** on every single resource so you can check for updates (and thus make your caching efficient)
175+
176+
License
177+
-------
178+
179+
PyRoar is `free software`_. Licensed mainly under the General Public License (GPL_).
180+
For more details on the licensing take a look at the LICENSE.txt file.
181+
182+
Trivia
183+
------
184+
185+
- This project is named after the Pokémon `Pyroar`_.
186+
187+
188+
.. _Pokéapi: https://pokeapi.co/
189+
.. _Pyroar: http://bulbapedia.bulbagarden.net/wiki/Pyroar_(Pok%C3%A9mon)
190+
.. _Pokémon: https://en.wikipedia.org/wiki/Pok%C3%A9mon
191+
.. _Pokéapi V2 Documentation: https://pokeapi.co/docsv2/
192+
.. _free software: http://en.wikipedia.org/wiki/Free_software
193+
.. _GPL: http://www.gnu.org/copyleft/gpl.html

pyroar/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
__author__ = "Daniel Perez-Gil"
2+
__copyright__ = "Copyright 2016, Daniel Perez-Gil"
3+
__credits__ = [""]
4+
__license__ = "GPL-3+"
5+
__version__ = "0.1"
6+
__maintainer__ = "Daniel Perez-Gil"
7+
__email__ = "[email protected]"
8+
__status__ = "Development"

pyroar/clientfactory.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from pyroar.commons import get_response
2+
3+
4+
class RestClient(object):
5+
"""Base client class"""
6+
def __init__(self, configuration, resource):
7+
self._configuration = configuration
8+
self._resource = resource
9+
10+
def get(self, query_id):
11+
"""Queries the REST service and returns the result"""
12+
response = get_response(host=self._configuration.host,
13+
version=self._configuration.version,
14+
resource=self._resource,
15+
query_id=query_id)
16+
17+
return response.json()
18+
19+
def get_methods(self):
20+
"""Retrieves the methods of the class"""
21+
ms = [method for method in dir(self) if callable(getattr(self,
22+
method))]
23+
return [m for m in ms if not m.startswith('_')]
24+
25+
26+
class ClientFactory(object):
27+
"""REST client factory"""
28+
def __init__(self):
29+
pass
30+
31+
def get_client(self, resource, configuration):
32+
"""Creates a new client for a given REST resource"""
33+
return self._get_custom_class(resource, configuration)
34+
35+
def _get_custom_class(self, name, configuration):
36+
"""Creates a new REST client and adds methods to it"""
37+
# Creating the class and instantiating it
38+
CustomClass = self._create_class(name)
39+
custom_class = CustomClass(configuration)
40+
# Adding custom methods to the class
41+
for value in custom_class.get("1").keys():
42+
self._add_method(CustomClass, str(value))
43+
return custom_class
44+
45+
def _add_method(self, cls, i):
46+
"""Adds a new method to a custom class"""
47+
def inner_method(self, query_id):
48+
return self.get(query_id)[i]
49+
inner_method.__doc__ = "docstring for get_%s" % i
50+
inner_method.__name__ = "get_%s" % i
51+
setattr(cls, inner_method.__name__, inner_method)
52+
53+
def _create_class(self, resource, RestClient=RestClient):
54+
"""Creates a new client class from RestClient"""
55+
def __init__(self, configuration):
56+
RestClient.__init__(self, configuration, resource)
57+
new_class = type(resource, (RestClient,), {"__init__": __init__})
58+
return new_class

pyroar/commons.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import requests
2+
3+
4+
def _create_rest_url(host, version, resource, query_id):
5+
"""Creates the URL for querying the REST service"""
6+
pokeapi_rest = 'api'
7+
8+
# Creating the basic URL
9+
url_parts = [host, pokeapi_rest, version]
10+
11+
# Checking for reource and query_id
12+
if resource is not None:
13+
url_parts.append(resource)
14+
if query_id is not None:
15+
url_parts.append(query_id)
16+
17+
url = ('http://' + '/'.join(url_parts))
18+
return url
19+
20+
21+
def get_response(host, version, resource=None, query_id=None):
22+
"""Creates the URL for querying the REST service"""
23+
url = _create_rest_url(host, version, resource, query_id)
24+
response = requests.get(url, headers={"Accept-Encoding": "gzip"})
25+
26+
# print(url)
27+
return response

pyroar/configclient.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import json
2+
import yaml
3+
4+
5+
class ConfigClient(object):
6+
"""Sets up the default configuration for the CellBase client"""
7+
def __init__(self, config_fpath=None):
8+
# Default config params
9+
self._host = 'pokeapi.co'
10+
self._version = 'v2'
11+
12+
# If config file is provided, override default config params
13+
if config_fpath is not None:
14+
self._override_config_params(config_fpath)
15+
16+
def _override_config_params(self, config_fpath):
17+
"""Overrides config params if config file is provided"""
18+
config_fhand = open(config_fpath, 'r')
19+
20+
config_dict = None
21+
if config_fpath.endswith('.yml') or config_fpath.endswith('.yaml'):
22+
config_dict = yaml.safe_load(config_fhand)
23+
24+
if config_fpath.endswith('.json'):
25+
config_dict = json.loads(config_fhand.read())
26+
27+
if config_dict is not None:
28+
if 'host' in config_dict:
29+
self._host = config_dict['host']
30+
if 'version' in config_dict:
31+
self._version = config_dict['version']
32+
33+
config_fhand.close()
34+
35+
@property
36+
def version(self):
37+
return self._version
38+
39+
@version.setter
40+
def version(self, new_version):
41+
self._version = new_version
42+
43+
@property
44+
def host(self):
45+
return self._host
46+
47+
@host.setter
48+
def host(self, new_host):
49+
self._host = new_host

0 commit comments

Comments
 (0)