├── .circleci └── config.yml ├── .gitignore ├── .pylintrc ├── LICENSE ├── Makefile ├── README.md ├── docs ├── authors.md ├── contributing.md ├── history.md ├── index.md ├── installation.md ├── stylesheets │ └── extra.css └── usage.md ├── make.bat ├── mkdocs.yml ├── pokepy ├── __init__.py ├── api.py ├── fcache │ ├── __init__.py │ ├── cache.py │ └── posixemulation.py └── resources_v2.py ├── requirements-dev.txt ├── requirements-dev27.txt ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── test_pokepy.py └── tox.ini /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at circleci.com 2 | 3 | 4 | version: 2.1 5 | 6 | 7 | orbs: 8 | windows: circleci/windows@2.4.0 9 | 10 | 11 | # ------------------------- 12 | # COMMANDS 13 | # ------------------------- 14 | commands: 15 | 16 | tests-only: 17 | steps: 18 | - checkout 19 | - run: 20 | name: Install Requirements 21 | command: pip install -r requirements.txt -r requirements-dev27.txt --user 22 | - run: 23 | name: Run Tests 24 | command: make test 25 | 26 | tests-coverage-lint: 27 | steps: 28 | - checkout 29 | - run: 30 | name: Install Requirements 31 | command: sudo pip install -r requirements.txt -r requirements-dev.txt 32 | - run: 33 | name: Lint 34 | command: make lint 35 | - run: 36 | name: Run Tests 37 | command: make test 38 | - run: 39 | name: Run Coverage tool 40 | command: coverage run --source pokepy --omit="pokepy/fcache/*" -m unittest tests.test_pokepy 41 | - run: 42 | name: Generate XML coverage report 43 | command: coverage xml 44 | - run: 45 | name: Upload Coverage report to CodeCov 46 | command: bash <(curl -s https://codecov.io/bash) 47 | 48 | 49 | # ------------------------- 50 | # JOBS 51 | # ------------------------- 52 | jobs: 53 | 54 | py27-linux: 55 | docker: 56 | - image: circleci/python:2.7.18 57 | steps: 58 | - tests-only 59 | 60 | py34-linux: 61 | docker: 62 | - image: circleci/python:3.4.10 63 | steps: 64 | - tests-only 65 | 66 | py35-linux: 67 | docker: 68 | - image: circleci/python:3.5.8 69 | steps: 70 | - tests-only 71 | 72 | py36-linux: 73 | docker: 74 | - image: circleci/python:3.6.10 75 | steps: 76 | - tests-only 77 | 78 | py37-linux: 79 | docker: 80 | - image: circleci/python:3.7.7 81 | steps: 82 | - tests-coverage-lint 83 | 84 | py38-linux: 85 | docker: 86 | - image: circleci/python:3.8.3 87 | steps: 88 | - tests-only 89 | 90 | py37-windows: 91 | executor: 92 | name: windows/default 93 | shell: cmd.exe 94 | steps: 95 | - tests-only 96 | 97 | py27-windows: 98 | executor: 99 | name: windows/default 100 | shell: cmd.exe 101 | steps: 102 | - checkout 103 | - run: 104 | name: Install Python 2.7 105 | command: choco install python2 106 | - run: 107 | name: Install Requirements 108 | command: C:\Python27\Scripts\pip.exe --version && C:\Python27\Scripts\pip.exe install -r requirements.txt -r requirements-dev27.txt --user 109 | - run: 110 | name: Run Tests 111 | command: C:\Python27\python.exe --version && C:\Python27\python.exe -m unittest tests.test_pokepy 112 | 113 | 114 | # ------------------------- 115 | # WORKFLOW 116 | # ------------------------- 117 | workflows: 118 | version: 2 119 | run_tests: 120 | jobs: 121 | - py27-linux 122 | - py34-linux 123 | - py35-linux 124 | - py36-linux 125 | - py37-linux 126 | - py38-linux 127 | - py37-windows 128 | - py27-windows 129 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg* 8 | *.cache 9 | *.egg-info 10 | dist 11 | build 12 | eggs 13 | parts 14 | bin 15 | var 16 | sdist 17 | develop-eggs 18 | .installed.cfg 19 | lib 20 | lib64 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | 29 | # html_coverage 30 | html_coverage/ 31 | 32 | # Pycharm 33 | .idea/ 34 | 35 | # virtual environment 36 | venv/ 37 | 38 | # Mkdocs build dir 39 | site/ 40 | 41 | package-lock.json 42 | node_modules -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code. 6 | extension-pkg-whitelist= 7 | 8 | # Add files or directories to the blacklist. They should be base names, not 9 | # paths. 10 | ignore=docs 11 | 12 | # Add files or directories matching the regex patterns to the blacklist. The 13 | # regex matches against base names, not paths. 14 | ignore-patterns= 15 | 16 | # Python code to execute, usually for sys.path manipulation such as 17 | # pygtk.require(). 18 | #init-hook= 19 | 20 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 21 | # number of processors available to use. 22 | jobs=0 23 | 24 | # Control the amount of potential inferred values when inferring a single 25 | # object. This can help the performance when dealing with large functions or 26 | # complex, nested conditions. 27 | limit-inference-results=100 28 | 29 | # List of plugins (as comma separated values of python modules names) to load, 30 | # usually to register additional checkers. 31 | load-plugins= 32 | 33 | # Pickle collected data for later comparisons. 34 | persistent=yes 35 | 36 | # Specify a configuration file. 37 | #rcfile= 38 | 39 | # When enabled, pylint would attempt to guess common misconfiguration and emit 40 | # user-friendly hints instead of false-positive error messages. 41 | suggestion-mode=yes 42 | 43 | # Allow loading of arbitrary C extensions. Extensions are imported into the 44 | # active Python interpreter and may run arbitrary code. 45 | unsafe-load-any-extension=no 46 | 47 | 48 | [MESSAGES CONTROL] 49 | 50 | # Only show warnings with the listed confidence levels. Leave empty to show 51 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 52 | confidence= 53 | 54 | # Disable the message, report, category or checker with the given id(s). You 55 | # can either give multiple identifiers separated by comma (,) or put this 56 | # option multiple times (only on the command line, not in the configuration 57 | # file where it should appear only once). You can also use "--disable=all" to 58 | # disable everything first and then reenable specific checks. For example, if 59 | # you want to run only the similarities checker, you can use "--disable=all 60 | # --enable=similarities". If you want to run only the classes checker, but have 61 | # no Warning level messages displayed, use "--disable=all --enable=classes 62 | # --disable=W". 63 | disable=missing-docstring, 64 | function-redefined, 65 | too-many-lines, 66 | no-member, 67 | keyword-arg-before-vararg, 68 | fixme, 69 | too-many-instance-attributes, 70 | 71 | # Disabled by default: 72 | 73 | print-statement, 74 | parameter-unpacking, 75 | unpacking-in-except, 76 | old-raise-syntax, 77 | backtick, 78 | long-suffix, 79 | old-ne-operator, 80 | old-octal-literal, 81 | import-star-module-level, 82 | non-ascii-bytes-literal, 83 | raw-checker-failed, 84 | bad-inline-option, 85 | locally-disabled, 86 | locally-enabled, 87 | file-ignored, 88 | suppressed-message, 89 | useless-suppression, 90 | deprecated-pragma, 91 | use-symbolic-message-instead, 92 | apply-builtin, 93 | basestring-builtin, 94 | buffer-builtin, 95 | cmp-builtin, 96 | coerce-builtin, 97 | execfile-builtin, 98 | file-builtin, 99 | long-builtin, 100 | raw_input-builtin, 101 | reduce-builtin, 102 | standarderror-builtin, 103 | unicode-builtin, 104 | xrange-builtin, 105 | coerce-method, 106 | delslice-method, 107 | getslice-method, 108 | setslice-method, 109 | no-absolute-import, 110 | old-division, 111 | dict-iter-method, 112 | dict-view-method, 113 | next-method-called, 114 | metaclass-assignment, 115 | indexing-exception, 116 | raising-string, 117 | reload-builtin, 118 | oct-method, 119 | hex-method, 120 | nonzero-method, 121 | cmp-method, 122 | input-builtin, 123 | round-builtin, 124 | intern-builtin, 125 | unichr-builtin, 126 | map-builtin-not-iterating, 127 | zip-builtin-not-iterating, 128 | range-builtin-not-iterating, 129 | filter-builtin-not-iterating, 130 | using-cmp-argument, 131 | eq-without-hash, 132 | div-method, 133 | idiv-method, 134 | rdiv-method, 135 | exception-message-attribute, 136 | invalid-str-codec, 137 | sys-max-int, 138 | bad-python3-import, 139 | deprecated-string-function, 140 | deprecated-str-translate-call, 141 | deprecated-itertools-function, 142 | deprecated-types-field, 143 | next-method-defined, 144 | dict-items-not-iterating, 145 | dict-keys-not-iterating, 146 | dict-values-not-iterating, 147 | deprecated-operator-function, 148 | deprecated-urllib-function, 149 | xreadlines-attribute, 150 | deprecated-sys-function, 151 | exception-escape, 152 | comprehension-escape 153 | 154 | # Enable the message, report, category or checker with the given id(s). You can 155 | # either give multiple identifier separated by comma (,) or put this option 156 | # multiple time (only on the command line, not in the configuration file where 157 | # it should appear only once). See also the "--disable" option for examples. 158 | enable=c-extension-no-member 159 | 160 | 161 | [REPORTS] 162 | 163 | # Python expression which should return a note less than 10 (10 is the highest 164 | # note). You have access to the variables errors warning, statement which 165 | # respectively contain the number of errors / warnings messages and the total 166 | # number of statements analyzed. This is used by the global evaluation report 167 | # (RP0004). 168 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 169 | 170 | # Template used to display messages. This is a python new-style format string 171 | # used to format the message information. See doc for all details. 172 | #msg-template= 173 | 174 | # Set the output format. Available formats are text, parseable, colorized, json 175 | # and msvs (visual studio). You can also give a reporter class, e.g. 176 | # mypackage.mymodule.MyReporterClass. 177 | output-format=text 178 | 179 | # Tells whether to display a full report or only the messages. 180 | reports=no 181 | 182 | # Activate the evaluation score. 183 | score=yes 184 | 185 | 186 | [REFACTORING] 187 | 188 | # Maximum number of nested blocks for function / method body 189 | max-nested-blocks=5 190 | 191 | # Complete name of functions that never returns. When checking for 192 | # inconsistent-return-statements if a never returning function is called then 193 | # it will be considered as an explicit return statement and no message will be 194 | # printed. 195 | never-returning-functions=sys.exit 196 | 197 | 198 | [BASIC] 199 | 200 | # Naming style matching correct argument names. 201 | argument-naming-style=snake_case 202 | 203 | # Regular expression matching correct argument names. Overrides argument- 204 | # naming-style. 205 | #argument-rgx= 206 | 207 | # Naming style matching correct attribute names. 208 | attr-naming-style=snake_case 209 | 210 | # Regular expression matching correct attribute names. Overrides attr-naming- 211 | # style. 212 | #attr-rgx= 213 | 214 | # Bad variable names which should always be refused, separated by a comma. 215 | bad-names=foo, 216 | bar, 217 | baz, 218 | toto, 219 | tutu, 220 | tata 221 | 222 | # Naming style matching correct class attribute names. 223 | class-attribute-naming-style=any 224 | 225 | # Regular expression matching correct class attribute names. Overrides class- 226 | # attribute-naming-style. 227 | #class-attribute-rgx= 228 | 229 | # Naming style matching correct class names. 230 | class-naming-style=PascalCase 231 | 232 | # Regular expression matching correct class names. Overrides class-naming- 233 | # style. 234 | #class-rgx= 235 | 236 | # Naming style matching correct constant names. 237 | const-naming-style=snake_case 238 | 239 | # Regular expression matching correct constant names. Overrides const-naming- 240 | # style. 241 | #const-rgx= 242 | 243 | # Minimum line length for functions/classes that require docstrings, shorter 244 | # ones are exempt. 245 | docstring-min-length=-1 246 | 247 | # Naming style matching correct function names. 248 | function-naming-style=snake_case 249 | 250 | # Regular expression matching correct function names. Overrides function- 251 | # naming-style. 252 | #function-rgx= 253 | 254 | # Good variable names which should always be accepted, separated by a comma. 255 | good-names=i, 256 | j, 257 | k, 258 | ex, 259 | Run, 260 | _ 261 | 262 | # Include a hint for the correct naming format with invalid-name. 263 | include-naming-hint=yes 264 | 265 | # Naming style matching correct inline iteration names. 266 | inlinevar-naming-style=any 267 | 268 | # Regular expression matching correct inline iteration names. Overrides 269 | # inlinevar-naming-style. 270 | #inlinevar-rgx= 271 | 272 | # Naming style matching correct method names. 273 | method-naming-style=snake_case 274 | 275 | # Regular expression matching correct method names. Overrides method-naming- 276 | # style. 277 | #method-rgx= 278 | 279 | # Naming style matching correct module names. 280 | module-naming-style=snake_case 281 | 282 | # Regular expression matching correct module names. Overrides module-naming- 283 | # style. 284 | #module-rgx= 285 | 286 | # Colon-delimited sets of names that determine each other's naming style when 287 | # the name regexes allow several styles. 288 | name-group= 289 | 290 | # Regular expression which should only match function or class names that do 291 | # not require a docstring. 292 | no-docstring-rgx=^_ 293 | 294 | # List of decorators that produce properties, such as abc.abstractproperty. Add 295 | # to this list to register other decorators that produce valid properties. 296 | # These decorators are taken in consideration only for invalid-name. 297 | property-classes=abc.abstractproperty 298 | 299 | # Naming style matching correct variable names. 300 | variable-naming-style=snake_case 301 | 302 | # Regular expression matching correct variable names. Overrides variable- 303 | # naming-style. 304 | variable-rgx=[a-z0-9_]{1,75}$ 305 | 306 | 307 | [FORMAT] 308 | 309 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 310 | expected-line-ending-format= 311 | 312 | # Regexp for a line that is allowed to be longer than the limit. 313 | ignore-long-lines=^\s*(# )??$ 314 | 315 | # Number of spaces of indent required inside a hanging or continued line. 316 | indent-after-paren=4 317 | 318 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 319 | # tab). 320 | indent-string=' ' 321 | 322 | # Maximum number of characters on a single line. 323 | max-line-length=100 324 | 325 | # Maximum number of lines in a module. 326 | max-module-lines=1000 327 | 328 | # List of optional constructs for which whitespace checking is disabled. `dict- 329 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 330 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 331 | # `empty-line` allows space-only lines. 332 | no-space-check=trailing-comma, 333 | dict-separator 334 | 335 | # Allow the body of a class to be on the same line as the declaration if body 336 | # contains single statement. 337 | single-line-class-stmt=no 338 | 339 | # Allow the body of an if to be on the same line as the test if there is no 340 | # else. 341 | single-line-if-stmt=no 342 | 343 | 344 | [LOGGING] 345 | 346 | # Logging modules to check that the string format arguments are in logging 347 | # function parameter format. 348 | logging-modules=logging 349 | 350 | 351 | [MISCELLANEOUS] 352 | 353 | # List of note tags to take in consideration, separated by a comma. 354 | notes=FIXME, 355 | XXX, 356 | TODO 357 | 358 | 359 | [SIMILARITIES] 360 | 361 | # Ignore comments when computing similarities. 362 | ignore-comments=yes 363 | 364 | # Ignore docstrings when computing similarities. 365 | ignore-docstrings=yes 366 | 367 | # Ignore imports when computing similarities. 368 | ignore-imports=no 369 | 370 | # Minimum lines number of a similarity. 371 | min-similarity-lines=4 372 | 373 | 374 | [SPELLING] 375 | 376 | # Limits count of emitted suggestions for spelling mistakes. 377 | max-spelling-suggestions=4 378 | 379 | # Spelling dictionary name. Available dictionaries: none. To make it working 380 | # install python-enchant package.. 381 | spelling-dict= 382 | 383 | # List of comma separated words that should not be checked. 384 | spelling-ignore-words= 385 | 386 | # A path to a file that contains private dictionary; one word per line. 387 | spelling-private-dict-file= 388 | 389 | # Tells whether to store unknown words to indicated private dictionary in 390 | # --spelling-private-dict-file option instead of raising a message. 391 | spelling-store-unknown-words=no 392 | 393 | 394 | [TYPECHECK] 395 | 396 | # List of decorators that produce context managers, such as 397 | # contextlib.contextmanager. Add to this list to register other decorators that 398 | # produce valid context managers. 399 | contextmanager-decorators=contextlib.contextmanager 400 | 401 | # List of members which are set dynamically and missed by pylint inference 402 | # system, and so shouldn't trigger E1101 when accessed. Python regular 403 | # expressions are accepted. 404 | generated-members= 405 | 406 | # Tells whether missing members accessed in mixin class should be ignored. A 407 | # mixin class is detected if its name ends with "mixin" (case insensitive). 408 | ignore-mixin-members=yes 409 | 410 | # Tells whether to warn about missing members when the owner of the attribute 411 | # is inferred to be None. 412 | ignore-none=yes 413 | 414 | # This flag controls whether pylint should warn about no-member and similar 415 | # checks whenever an opaque object is returned when inferring. The inference 416 | # can return multiple potential results while evaluating a Python object, but 417 | # some branches might not be evaluated, which results in partial inference. In 418 | # that case, it might be useful to still emit no-member and other checks for 419 | # the rest of the inferred objects. 420 | ignore-on-opaque-inference=yes 421 | 422 | # List of class names for which member attributes should not be checked (useful 423 | # for classes with dynamically set attributes). This supports the use of 424 | # qualified names. 425 | ignored-classes=optparse.Values,thread._local,_thread._local 426 | 427 | # List of module names for which member attributes should not be checked 428 | # (useful for modules/projects where namespaces are manipulated during runtime 429 | # and thus existing member attributes cannot be deduced by static analysis. It 430 | # supports qualified module names, as well as Unix pattern matching. 431 | ignored-modules= 432 | 433 | # Show a hint with possible names when a member name was not found. The aspect 434 | # of finding the hint is based on edit distance. 435 | missing-member-hint=yes 436 | 437 | # The minimum edit distance a name should have in order to be considered a 438 | # similar match for a missing member name. 439 | missing-member-hint-distance=1 440 | 441 | # The total number of similar names that should be taken in consideration when 442 | # showing a hint for a missing member. 443 | missing-member-max-choices=1 444 | 445 | 446 | [VARIABLES] 447 | 448 | # List of additional names supposed to be defined in builtins. Remember that 449 | # you should avoid to define new builtins when possible. 450 | additional-builtins= 451 | 452 | # Tells whether unused global variables should be treated as a violation. 453 | allow-global-unused-variables=yes 454 | 455 | # List of strings which can identify a callback function by name. A callback 456 | # name must start or end with one of those strings. 457 | callbacks=cb_, 458 | _cb 459 | 460 | # A regular expression matching the name of dummy variables (i.e. expected to 461 | # not be used). 462 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 463 | 464 | # Argument names that match this expression will be ignored. Default to name 465 | # with leading underscore. 466 | ignored-argument-names=_.*|^ignored_|^unused_ 467 | 468 | # Tells whether we should check for unused import in __init__ files. 469 | init-import=no 470 | 471 | # List of qualified module names which can have objects that can redefine 472 | # builtins. 473 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 474 | 475 | 476 | [CLASSES] 477 | 478 | # List of method names used to declare (i.e. assign) instance attributes. 479 | defining-attr-methods=__init__, 480 | __new__, 481 | setUp 482 | 483 | # List of member names, which should be excluded from the protected access 484 | # warning. 485 | exclude-protected=_asdict, 486 | _fields, 487 | _replace, 488 | _source, 489 | _make 490 | 491 | # List of valid names for the first argument in a class method. 492 | valid-classmethod-first-arg=cls 493 | 494 | # List of valid names for the first argument in a metaclass class method. 495 | valid-metaclass-classmethod-first-arg=cls 496 | 497 | 498 | [DESIGN] 499 | 500 | # Maximum number of arguments for function / method. 501 | max-args=10 # default = 5 502 | 503 | # Maximum number of attributes for a class (see R0902). 504 | max-attributes=7 505 | 506 | # Maximum number of boolean expressions in an if statement. 507 | max-bool-expr=5 508 | 509 | # Maximum number of branch for function / method body. 510 | max-branches=12 511 | 512 | # Maximum number of locals for function / method body. 513 | max-locals=15 514 | 515 | # Maximum number of parents for a class (see R0901). 516 | max-parents=7 517 | 518 | # Maximum number of public methods for a class (see R0904). 519 | max-public-methods=500 # default = 20 520 | 521 | # Maximum number of return / yield for function / method body. 522 | max-returns=6 523 | 524 | # Maximum number of statements in function / method body. 525 | max-statements=50 526 | 527 | # Minimum number of public methods for a class (see R0903). 528 | min-public-methods=0 # default = 2 529 | 530 | 531 | [IMPORTS] 532 | 533 | # Allow wildcard imports from modules that define __all__. 534 | allow-wildcard-with-all=no 535 | 536 | # Analyse import fallback blocks. This can be used to support both Python 2 and 537 | # 3 compatible code, which means that the block might have code that exists 538 | # only in one or another interpreter, leading to false positives when analysed. 539 | analyse-fallback-blocks=no 540 | 541 | # Deprecated modules which should not be used, separated by a comma. 542 | deprecated-modules=optparse,tkinter.tix 543 | 544 | # Create a graph of external dependencies in the given file (report RP0402 must 545 | # not be disabled). 546 | ext-import-graph= 547 | 548 | # Create a graph of every (i.e. internal and external) dependencies in the 549 | # given file (report RP0402 must not be disabled). 550 | import-graph= 551 | 552 | # Create a graph of internal dependencies in the given file (report RP0402 must 553 | # not be disabled). 554 | int-import-graph= 555 | 556 | # Force import order to recognize a module as part of the standard 557 | # compatibility libraries. 558 | known-standard-library= 559 | 560 | # Force import order to recognize a module as part of a third party library. 561 | known-third-party=enchant 562 | 563 | 564 | [EXCEPTIONS] 565 | 566 | # Exceptions that will emit a warning when being caught. Defaults to 567 | # "Exception". 568 | overgeneral-exceptions=Exception 569 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Paul Hallett 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of Pokepy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help clean clean-build clean-pyc clean-tests clean-docs lint test test-all coverage docs-build docs-test docs-release sdist sdist-test release 2 | 3 | help: 4 | @echo "clean removes build and python file artifacts" 5 | @echo "clean-build removes build artifacts" 6 | @echo "clean-pyc removes python file artifacts" 7 | @echo "clean-tests removes test and coverage files" 8 | @echo "clean-docs removes built docs files" 9 | @echo "lint checks style with pylint" 10 | @echo "test runs tests quickly with the default python version" 11 | @echo "test-all runs tests on every python version with tox" 12 | @echo "coverage checks code coverage quickly with the default python version" 13 | @echo "docs-build builds MkDocs HTML documentation" 14 | @echo "docs-test live tests the current documentation" 15 | @echo "docs-release pushes built docs to gh-pages branch of github repo (git should exist on PATH)" 16 | @echo "sdist packages source distribution package" 17 | @echo "sdist-test looks for errors on the source distribution package" 18 | @echo "release packages and uploads a release" 19 | 20 | clean: clean-build clean-pyc clean-tests clean-docs 21 | 22 | clean-build: 23 | rm -rf build/ 24 | rm -rf dist/ 25 | rm -rf pokepy.egg-info/ 26 | 27 | clean-pyc: 28 | find . -name '*.pyc' -exec rm -f {} + 29 | find . -name '*.pyo' -exec rm -f {} + 30 | find . -name '*~' -exec rm -f {} + 31 | 32 | clean-tests: 33 | rm -rf .tox/ 34 | rm -rf html_coverage/ 35 | 36 | clean-docs: 37 | rm -rf site/ 38 | 39 | lint: 40 | python -m pylint --ignore=pokepy/fcache pokepy tests setup.py 41 | 42 | test: 43 | python -m unittest tests.test_pokepy 44 | 45 | test-all: 46 | tox 47 | 48 | coverage: 49 | coverage run --source pokepy --omit="pokepy/fcache/*" -m unittest tests.test_pokepy 50 | coverage report -m 51 | coverage html -d html_coverage 52 | open htmlcov/index.html 53 | 54 | docs-build: 55 | mkdocs build 56 | open site/index.html 57 | 58 | docs-test: 59 | mkdocs serve 60 | 61 | docs-release: 62 | mkdocs gh-deploy --verbose 63 | 64 | sdist: clean-build clean-pyc 65 | python setup.py sdist bdist_wheel 66 | 67 | sdist-test: 68 | twine check dist/* 69 | twine upload --repository-url https://test.pypi.org/legacy/ dist/* 70 | 71 | release: sdist 72 | twine upload dist/* 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pokepy 2 | 3 | [![pypi](https://img.shields.io/pypi/v/pokepy.svg "pypi package")](https://pypi.org/project/pokepy) 4 | [![python versions](https://img.shields.io/pypi/pyversions/pokepy.svg "supported python versions")](https://pypi.org/project/pokepy) 5 | [![downloads](https://img.shields.io/pypi/dm/pokepy.svg?style=popout "pypi downloads")](https://pypi.org/project/pokepy/) 6 | [![build status](https://circleci.com/gh/PokeAPI/pokepy.svg?style=svg "build status")](https://circleci.com/gh/PokeAPI/pokepy) 7 | [![coverage](https://codecov.io/gh/PokeAPI/pokepy/branch/master/graph/badge.svg "code coverage")](https://codecov.io/gh/PokeAPI/pokepy) 8 | [![snyk](https://snyk.io/test/github/PokeAPI/pokepy/badge.svg?targetFile=requirements.txt "known vulnerabilities")](https://snyk.io/test/github/PokeAPI/pokepy?targetFile=requirements.txt) 9 | [![license](https://img.shields.io/pypi/l/pokepy.svg "license")](https://github.com/PokeAPI/pokepy/blob/master/LICENSE) 10 | 11 | A python wrapper for [PokéAPI](https://pokeapi.co). (former [pykemon](https://github.com/PokeAPI/pokepy/tree/bb72105f4c5402aaa5d4fd2b9c142bf9b678b254)) 12 | 13 | Maintainer: [Kronopt](https://github.com/Kronopt) 14 | 15 | ## Installation 16 | 17 | Nice and simple: 18 | 19 | ```sh 20 | $ pip install pokepy 21 | ``` 22 | 23 | ## Usage 24 | 25 | Even simpler: 26 | 27 | ```python 28 | >>> import pokepy 29 | >>> client = pokepy.V2Client() 30 | >>> client.get_pokemon(14) 31 | 32 | ``` 33 | 34 | ## Documentation 35 | 36 | For more information, check the documentation at https://pokeapi.github.io/pokepy 37 | 38 | ## Features 39 | 40 | * Generate Python objects from PokéAPI resources 41 | * Cache 42 | * Human-friendly API 43 | -------------------------------------------------------------------------------- /docs/authors.md: -------------------------------------------------------------------------------- 1 | ### Original Project Lead (Pykemon) 2 | * [Paul Hallett](https://github.com/phalt) 3 | 4 | ### Maintainer 5 | * [Kronopt](https://github.com/Kronopt) 6 | 7 | ### Contributors 8 | * [Owen Hallett](https://github.com/Videocard) 9 | * [Kronopt](https://github.com/Kronopt) 10 | * [Naramsim](https://github.com/Naramsim) 11 | 12 | Made a commit? Add your name to the list! 13 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Contributions are welcome, and they are greatly appreciated! Every 3 | little bit helps, and credit will always be given. 4 | 5 | You can contribute in many ways: 6 | 7 | ### Reporting Bugs 8 | Report bugs in the [issues section](https://github.com/PokeAPI/pokepy/issues). 9 | 10 | When reporting a bug, please include: 11 | 12 | * Your operating system name and version. 13 | * Any details about your local setup that might be helpful in troubleshooting. 14 | * Detailed steps to reproduce the bug. 15 | 16 | ### Fixing Bugs 17 | Look through the [issues section](https://github.com/PokeAPI/pokepy/issues) for bugs. Anything tagged with `bug` 18 | is open to whoever wants to implement it. 19 | 20 | ### Implementing Features 21 | Look through the [issues section](https://github.com/PokeAPI/pokepy/issues) for features. Anything tagged with `feature` 22 | is open to whoever wants to implement it. 23 | 24 | When proposing a feature: 25 | 26 | * Explain in detail how it would work. 27 | * Keep the scope as narrow as possible, to make it easier to implement. 28 | 29 | ### Writing Documentation 30 | Pokepy could always use more documentation, whether as part of the 31 | official Pokepy docs, in docstrings, or even on the web in blog posts, 32 | articles, and such. 33 | 34 | ### Submiting Feedback 35 | The best way to send feedback is to file an issue in the [issues section](https://github.com/PokeAPI/pokepy/issues). 36 | 37 | Remember that this is a volunteer-driven project, and that contributions 38 | are welcome :) 39 | 40 | ### Get Started! 41 | Ready to contribute? Here's how to set up `pokepy` for local development. 42 | 43 | **1 .** Fork the `pokepy` repo on GitHub. 44 | 45 | **2 .** Clone your fork locally. You can use a tool like [Github Desktop](https://desktop.github.com/) 46 | or through the command line with `git`: 47 | 48 | ``` 49 | $ git clone git@github.com:your_name_here/pokepy.git 50 | ``` 51 | 52 | (you can also simply download the project from github as a .zip file) 53 | 54 | **3 .** Install your local copy into a virtual environment. 55 | If you use an IDE, it should have an option to create a new virtual environment. 56 | Otherwise, and assuming you have `virtualenv` installed, this is how you set up your fork for local development: 57 | 58 | ``` 59 | $ mkdir venv 60 | $ virtualenv venv/pokepy 61 | $ cd venv/pokepy/bin 62 | $ source activate 63 | $ pip install -r requirements.txt -r requirements-dev.txt 64 | ``` 65 | 66 | **4 .** Create a branch for local development (`git`): 67 | 68 | ``` 69 | $ git checkout -b name-of-your-bugfix-or-feature 70 | ``` 71 | 72 | Now you can make your changes locally. 73 | 74 | **5 .** When you're done making changes, check that your changes pass `pylint` and the `tests`, 75 | including testing other Python versions with `tox`: 76 | 77 | ``` 78 | $ python -m pylint pokepy tests setup.py 79 | $ tox 80 | ``` 81 | 82 | `pylint` and `tox` should already be installed in your virtualenv if you followed step 3 correctly. 83 | 84 | **6 .** Commit your changes and push your branch to GitHub (`git`): 85 | 86 | ``` 87 | $ git add . 88 | $ git commit -m "Your detailed description of your changes" 89 | $ git push origin name-of-your-bugfix-or-feature 90 | ``` 91 | 92 | **7 .** Submit a pull request through the GitHub website. 93 | 94 | ### Pull Request Guidelines 95 | Before submiting a pull request, check that it meets these guidelines: 96 | 97 | **1 .** The pull request includes tests (if relevant). 98 | 99 | **2 .** If the pull request adds functionality, the docs should be updated. 100 | Put your new functionality into a function with a docstring, and add the 101 | feature to the list in [README](https://github.com/PokeAPI/pokepy/blob/master/README.md). 102 | 103 | **3 .** The pull request should work for Python 2.7, 3.4, 3.5, 3.6, 3.7 and 3.8. 104 | 105 | ### Tips 106 | To run a subset of tests: 107 | 108 | ``` 109 | $ python -m unittest tests.test_pokepy 110 | ``` 111 | -------------------------------------------------------------------------------- /docs/history.md: -------------------------------------------------------------------------------- 1 | # History 2 | ### 0.6.2 (2021-05-16) 3 | * Add `version` subproperty to `FlavorTextSubResource` 4 | 5 | ### 0.6.1 (2020-05-31) 6 | * Fixed disk-based cache not handling different filesystems on Windows 7 | 8 | ### 0.6.0 (2019-05-3) 9 | * V2Client get methods now return element instead of single element list 10 | * set urllib3 version to >=1.24.3, <1.25 (CVE-2019-11236) 11 | * Support for Python 3.4 and 3.5 12 | 13 | ### 0.5.2 (2019-03-01) 14 | * Fixed bug that caused pokemon_encounters subresource to not be detected in LocationAreaResource 15 | (thanks to [jachymb](https://github.com/jachymb)) 16 | 17 | ### 0.5.1 (2019-02-16) 18 | * New V2Client cache-related methods: 19 | * cache_info 20 | * cache_clear 21 | * cache_location 22 | 23 | ### 0.5.0 (2019-01-19) 24 | * Pykemon is now Pokepy! 25 | * Cache (disk- and memory-based) 26 | 27 | ### 0.4.0 (2018-10-11) 28 | * Removed code from pre-beckett versions 29 | * Removed V1 API support, as it is now deprecated 30 | * Added some tweaks to the beckett package 31 | 32 | ### 0.3.0 (2017-10-19) 33 | * V2 support added 34 | * Added some missing V1 resources 35 | * Removed files related to API 0.1 36 | 37 | ### 0.2.0 (2016-06-11) 38 | * Beckett API Client framework added 39 | 40 | ### 0.1.2 (2014-1-3) 41 | * Sprite attribute added to Pokemon class 42 | 43 | ### 0.1.1 (2013-12-24) 44 | * Description attribute added to Pokemon class 45 | 46 | ### 0.1.0 (2013-12-23) 47 | * First release on PyPI 48 | * All PokéAPI resources fully supported and represented in an object-oriented style 49 | * Easy-to-use API: just one method! 50 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Pokepy 2 | 3 | [![pypi](https://img.shields.io/pypi/v/pokepy.svg "pypi package")](https://pypi.org/project/pokepy) 4 | [![python versions](https://img.shields.io/pypi/pyversions/pokepy.svg "supported python versions")](https://pypi.org/project/pokepy) 5 | [![downloads](https://img.shields.io/pypi/dm/pokepy.svg?style=popout "pypi downloads")](https://pypi.org/project/pokepy/) 6 | [![build status](https://circleci.com/gh/PokeAPI/pokepy.svg?style=svg "build status")](https://circleci.com/gh/PokeAPI/pokepy) 7 | [![coverage](https://codecov.io/gh/PokeAPI/pokepy/branch/master/graph/badge.svg "code coverage")](https://codecov.io/gh/PokeAPI/pokepy) 8 | [![snyk](https://snyk.io/test/github/PokeAPI/pokepy/badge.svg?targetFile=requirements.txt "known vulnerabilities")](https://snyk.io/test/github/PokeAPI/pokepy?targetFile=requirements.txt) 9 | [![license](https://img.shields.io/pypi/l/pokepy.svg "license")](https://github.com/PokeAPI/pokepy/blob/master/LICENSE) 10 | 11 | A python wrapper for [PokéAPI](https://pokeapi.co). (former [pykemon](https://github.com/PokeAPI/pokepy/tree/bb72105f4c5402aaa5d4fd2b9c142bf9b678b254)) 12 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | At the command line: 3 | ```bash 4 | $ pip install pokepy 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | .md-header { 2 | background: #ef5350; 3 | } 4 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | To use Pokepy in a project: 4 | 5 | ```python 6 | >>> import pokepy 7 | ``` 8 | 9 | ### API 10 | 11 | Pokepy is composed of a single class, `V2Client`, which implements the whole 12 | [v2 PokéAPI](https://pokeapi.co/docs/v2). 13 | This class is usually instantiated without parameters: 14 | 15 | ```python 16 | >>> client = pokepy.V2Client() 17 | ``` 18 | 19 | Unless you want to use the caching feature, which is discussed [further below](#cache). 20 | 21 | Each endpoint of PokéAPI is represented in `V2Client` by a `get_` method, 22 | all taking a single parameter (`uid`), which can be either an `integer` (for most endpoints) or a `string`. 23 | 24 | The following is an exhaustive list of all the endpoints with links to their respective PokéAPI documentation: 25 | 26 | - [get_berry](https://pokeapi.co/docs/v2#berries) 27 | - [get_berry_firmness](https://pokeapi.co/docs/v2#berry-firmnesses) 28 | - [get_berry_flavor](https://pokeapi.co/docs/v2#berry-flavors) 29 | - [get_contest_type](https://pokeapi.co/docs/v2#contest-types) 30 | - [get_contest_effect](https://pokeapi.co/docs/v2#contest-effects) 31 | - [get_super_contest_effect](https://pokeapi.co/docs/v2#super-contest-effects) 32 | - [get_encounter_method](https://pokeapi.co/docs/v2#encounter-methods) 33 | - [get_encounter_condition](https://pokeapi.co/docs/v2#encounter-conditions) 34 | - [get_encounter_condition_value](https://pokeapi.co/docs/v2#encounter-condition-values) 35 | - [get_evolution_chain](https://pokeapi.co/docs/v2#evolution-chains) 36 | - [get_evolution_trigger](https://pokeapi.co/docs/v2#evolution-triggers) 37 | - [get_generation](https://pokeapi.co/docs/v2#generations) 38 | - [get_pokedex](https://pokeapi.co/docs/v2#pokedexes) 39 | - [get_version](https://pokeapi.co/docs/v2#version) 40 | - [get_version_group](https://pokeapi.co/docs/v2#version-groups) 41 | - [get_item](https://pokeapi.co/docs/v2#item) 42 | - [get_item_attribute](https://pokeapi.co/docs/v2#item-attributes) 43 | - [get_item_category](https://pokeapi.co/docs/v2#item-categories) 44 | - [get_item_fling_effect](https://pokeapi.co/docs/v2#item-fling-effects) 45 | - [get_item_pocket](https://pokeapi.co/docs/v2#item-pockets) 46 | - [get_location](https://pokeapi.co/docs/v2#locations) 47 | - [get_location_area](https://pokeapi.co/docs/v2#location-areas) 48 | - [get_pal_park_area](https://pokeapi.co/docs/v2#pal-park-areas) 49 | - [get_region](https://pokeapi.co/docs/v2#regions) 50 | - [get_machine](https://pokeapi.co/docs/v2#machines) 51 | - [get_move](https://pokeapi.co/docs/v2#moves) 52 | - [get_move_ailment](https://pokeapi.co/docs/v2#move-ailments) 53 | - [get_move_battle_style](https://pokeapi.co/docs/v2#move-battle-styles) 54 | - [get_move_category](https://pokeapi.co/docs/v2#move-categories) 55 | - [get_move_damage_class](https://pokeapi.co/docs/v2#move-damage-classes) 56 | - [get_move_learn_method](https://pokeapi.co/docs/v2#move-learn-methods) 57 | - [get_move_target](https://pokeapi.co/docs/v2#move-targets) 58 | - [get_ability](https://pokeapi.co/docs/v2#abilities) 59 | - [get_characteristic](https://pokeapi.co/docs/v2#characteristics) 60 | - [get_egg_group](https://pokeapi.co/docs/v2#egg-groups) 61 | - [get_gender](https://pokeapi.co/docs/v2#genders) 62 | - [get_growth_rate](https://pokeapi.co/docs/v2#growth-rates) 63 | - [get_nature](https://pokeapi.co/docs/v2#natures) 64 | - [get_pokeathlon_stat](https://pokeapi.co/docs/v2#pokeathlon-stats) 65 | - [get_pokemon](https://pokeapi.co/docs/v2#pokemon) 66 | - [get_pokemon_color](https://pokeapi.co/docs/v2#pok%C3%A9mon-colors) 67 | - [get_pokemon_form](https://pokeapi.co/docs/v2#pok%C3%A9mon-forms) 68 | - [get_pokemon_habitat](https://pokeapi.co/docs/v2#pok%C3%A9mon-habitats) 69 | - [get_pokemon_shape](https://pokeapi.co/docs/v2#pok%C3%A9mon-shapes) 70 | - [get_pokemon_species](https://pokeapi.co/docs/v2#pok%C3%A9mon-species) 71 | - [get_stat](https://pokeapi.co/docs/v2#stats) 72 | - [get_type](https://pokeapi.co/docs/v2#types) 73 | - [get_language](https://pokeapi.co/docs/v2#languages) 74 | 75 | Each method returns an object containing as many python attributes as there are named attributes. 76 | Please refer to the [PokéAPI documentation](https://pokeapi.co/docs/v2) 77 | for more information on what each of these methods returns, its description and type. 78 | 79 | Then you can start grabbing stuff from the API: 80 | 81 | ```python 82 | >>> mew = pokepy.V2Client().get_pokemon('mew') 83 | >>> mew 84 | 85 | >>> mew.name 86 | mew 87 | ``` 88 | 89 | ```python 90 | >>> kakuna = pokepy.V2Client().get_pokemon(14) 91 | >>> kakuna 92 | 93 | >>> kakuna.weigth 94 | 100 95 | ``` 96 | 97 | ```python 98 | >>> cut = pokepy.V2Client().get_move(15) 99 | >>> cut 100 | 101 | >>> cut.power 102 | 50 103 | ``` 104 | 105 | Some resources have subresources: 106 | 107 | ```python 108 | >>> kakuna = pokepy.V2Client().get_pokemon(14) 109 | >>> kakuna 110 | 111 | >>> kakuna.types 112 | [, ] 113 | >>> kakuna.types[0].type.name 114 | poison 115 | ``` 116 | 117 | ```python 118 | >>> insomnia = pokepy.V2Client().get_ability(15) 119 | >>> insomnia 120 | 121 | >>> insomnia.effect_entries[0].short_effect 122 | Prevents sleep. 123 | ``` 124 | 125 | ### Parameters 126 | 127 | Most resources can be requested by using either the `name` or `id` of the resource: 128 | 129 | ```python 130 | >>> pokepy.V2Client().get_pokemon('rotom') 131 | 132 | >>> pokepy.V2Client().get_pokemon(479) 133 | 134 | >>> pokepy.V2Client().get_pokemon('479') 135 | 136 | ``` 137 | 138 | ### Cache 139 | 140 | If you use the API to get the same resources often, 141 | you can enable cache to avoid making unnecessary requests to the PokéAPI server. 142 | You can either enable `memory-based` or `disk-based` cache. 143 | 144 | #### Memory-based 145 | 146 | Memory-based cache is activated by passing `in_memory` to the `cache` parameter of `V2Client`. 147 | Resources obtained from the PokéAPI are then saved in RAM. Cache is kept per get method: 148 | 149 | ```python 150 | >>> client_mem_cache = pokepy.V2Client(cache='in_memory') 151 | ``` 152 | 153 | You can check the state of the cache in two ways: per get method or as a whole. 154 | 155 | To check the state of the cache of a particular method, call the `cache_info()` 156 | of that get method: 157 | 158 | ```python 159 | >>> client_mem_cache.get_pokemon.cache_info() 160 | CacheInfo(hits=0, misses=0, size=0) 161 | ``` 162 | 163 | To check the state of the cache as a whole (all get methods combined), 164 | call the `cache_info()` of `V2Client`: 165 | 166 | ```python 167 | >>> client_mem_cache.cache_info() 168 | CacheInfo(hits=0, misses=0, size=0) 169 | ``` 170 | 171 | `hits` is the number of previously cached parametes which were returned, 172 | `misses` is the number given parameters not previously cached (which are now cached), 173 | and `size` is the total number of cached parameters. 174 | 175 | When calling a certain endpoint, the `cache_info` reflects that call: 176 | 177 | ```python 178 | >>> kakuna = client_mem_cache.get_pokemon(14) 179 | >>> client_mem_cache.get_pokemon.cache_info() 180 | CacheInfo(hits=0, misses=1, size=1) 181 | ``` 182 | 183 | Calling the same resource as before with the same parameters will retrieve 184 | the cached resource instead of getting it from the server: 185 | 186 | ```python 187 | >>> kakuna = client_mem_cache.get_pokemon(14) 188 | >>> client_mem_cache.get_pokemon.cache_info() 189 | CacheInfo(hits=1, misses=1, size=1) 190 | ``` 191 | 192 | To clear the cache of a specific get method: 193 | 194 | ```python 195 | >>> client_mem_cache.get_pokemon.cache_clear() 196 | >>> client_mem_cache.get_pokemon.cache_info() 197 | CacheInfo(hits=0, misses=0, size=0) 198 | ``` 199 | 200 | To clear all cache: 201 | 202 | ```python 203 | >>> client_mem_cache.cache_clear() 204 | >>> client_mem_cache.cache_info() 205 | CacheInfo(hits=0, misses=0, size=0) 206 | ``` 207 | 208 | #### Disk-based 209 | 210 | Disk-based cache is activated by passing `in_disk` to the `cache` parameter of `V2Client`. 211 | Resources obtained from the PokéAPI are then saved to disk. Cache is kept per get method: 212 | 213 | ```python 214 | >>> client_disk_cache = pokepy.V2Client(cache='in_disk', cache_location='/temp') 215 | ``` 216 | 217 | In this case it's possible to specify the cache directory with the `cache_location` parameter. 218 | A folder named `pokepy_cache` will be created inside the specified directory, where the 219 | cache of each get method will be located. 220 | If no cache directory is specified a system-appropriate cache directory is automatically determined by 221 | [appdirs](https://pypi.org/project/appdirs/). 222 | 223 | The methods used to check the state and clear the cache are the same as in the memory-based cache, 224 | including the global `V2Client` methods. 225 | 226 | You can also check the cache directory, per get method: 227 | 228 | ```python 229 | >>> client_disk_cache.get_pokemon.cache_location() 230 | /temp/pokepy_cache/39/cache 231 | ``` 232 | 233 | Or check the global cache directory: 234 | 235 | ```python 236 | >>> client_disk_cache.cache_location() 237 | /temp/pokepy_cache/ 238 | ``` 239 | 240 | Disk-based cache is reloaded automatically between runs if the same cache directory is specified. 241 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if "%1" == "" goto help 4 | goto %~1 5 | 6 | :help 7 | echo. 8 | echo clean removes build and python file artifacts 9 | echo clean-build removes build artifacts 10 | echo clean-pyc removes python file artifacts 11 | echo clean-tests removes test and coverage files 12 | echo clean-docs removes built docs files 13 | echo lint checks style with pylint 14 | echo test runs tests quickly with the default python version 15 | echo test-all runs tests on every python version with tox 16 | echo coverage checks code coverage quickly with the default python version 17 | echo docs-build builds MkDocs HTML documentation 18 | echo docs-test live tests the current documentation 19 | echo docs-release pushes built docs to gh-pages branch of github repo (git should exist on PATH) 20 | echo sdist packages source distribution package 21 | echo sdist-test looks for errors on the source distribution package 22 | echo release packages and uploads a release 23 | goto:eof 24 | 25 | :clean 26 | call:clean-build 27 | call:clean-pyc 28 | call:clean-tests 29 | call:clean-docs 30 | goto:eof 31 | 32 | :clean-build 33 | rmdir /s /q build 34 | rmdir /s /q dist 35 | rmdir /s /q pokepy.egg-info 36 | goto:eof 37 | 38 | :clean-pyc 39 | del /s *.pyc *.pyo *~ 40 | goto:eof 41 | 42 | :clean-tests 43 | rmdir /s /q .tox 44 | rmdir /s /q html_coverage 45 | goto:eof 46 | 47 | :clean-docs 48 | rmdir /s /q site 49 | goto:eof 50 | 51 | :lint 52 | python -m pylint --ignore=pokepy\fcache pokepy tests setup.py 53 | goto:eof 54 | 55 | :test 56 | python -m unittest tests.test_pokepy 57 | goto:eof 58 | 59 | :test-all 60 | tox 61 | goto:eof 62 | 63 | :coverage 64 | coverage run --source pokepy --omit="pokepy\fcache\*" -m unittest tests.test_pokepy 65 | coverage report -m 66 | coverage html -d html_coverage 67 | start "" html_coverage/index.html 68 | goto:eof 69 | 70 | :docs-build 71 | mkdocs build 72 | start "" site/index.html 73 | goto:eof 74 | 75 | :docs-test 76 | mkdocs serve 77 | goto:eof 78 | 79 | :docs-release 80 | mkdocs gh-deploy --verbose 81 | goto:eof 82 | 83 | :sdist 84 | call:clean-build 85 | call:clean-pyc 86 | python setup.py sdist bdist_wheel 87 | goto:eof 88 | 89 | :sdist-test 90 | twine check dist/* 91 | twine upload --repository-url https://test.pypi.org/legacy/ dist/* 92 | goto:eof 93 | 94 | :release 95 | call:sdist 96 | twine upload dist/* 97 | goto:eof 98 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Pokepy 2 | theme: 3 | name: 'material' 4 | 5 | nav: 6 | - Home: 'index.md' 7 | - Installation: 'installation.md' 8 | - Usage: 'usage.md' 9 | - Contributing: 'contributing.md' 10 | - Authors: 'authors.md' 11 | - History: 'history.md' 12 | 13 | extra_css: 14 | - 'stylesheets/extra.css' 15 | -------------------------------------------------------------------------------- /pokepy/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | Pokepy 6 | 7 | A Python wrapper for PokéAPI (https://pokeapi.co) 8 | 9 | Usage: 10 | >>> import pokepy 11 | >>> clientV2 = pokepy.V2Client() 12 | >>> clientV2.get_pokemon('bulbasaur') 13 | 14 | """ 15 | 16 | __author__ = 'Paul Hallett' 17 | __email__ = 'hello@phalt.co' 18 | __credits__ = ["Paul Hallett", "Owen Hallett", "Kronopt"] 19 | __version__ = '0.6.2' 20 | __copyright__ = 'Copyright Paul Hallett 2016' 21 | __license__ = 'BSD' 22 | 23 | 24 | from .api import V2Client 25 | -------------------------------------------------------------------------------- /pokepy/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | pokepy.api 6 | 7 | User interaction with this package is done through this file. 8 | """ 9 | 10 | import functools 11 | import os 12 | import sys 13 | import types 14 | from collections import namedtuple 15 | import appdirs # dependency of FileCache 16 | from beckett.clients import BaseClient 17 | from beckett.constants import DEFAULT_VALID_STATUS_CODES 18 | from .fcache.cache import FileCache 19 | from . import resources_v2 as rv2 20 | from . import __version__ 21 | 22 | 23 | class V2Client(BaseClient): 24 | """Pokéapi client""" 25 | 26 | class Meta(BaseClient.Meta): 27 | name = 'pokepy-v2-client-' + __version__ 28 | base_url = 'https://pokeapi.co/api/v2' 29 | resources = ( 30 | rv2.BerryResource, 31 | rv2.BerryFirmnessResource, 32 | rv2.BerryFlavorResource, 33 | rv2.ContestTypeResource, 34 | rv2.ContestEffectResource, 35 | rv2.SuperContestEffectResource, 36 | rv2.EncounterMethodResource, 37 | rv2.EncounterConditionResource, 38 | rv2.EncounterConditionValueResource, 39 | rv2.EvolutionChainResource, 40 | rv2.EvolutionTriggerResource, 41 | rv2.GenerationResource, 42 | rv2.PokedexResource, 43 | rv2.VersionResource, 44 | rv2.VersionGroupResource, 45 | rv2.ItemResource, 46 | rv2.ItemAttributeResource, 47 | rv2.ItemCategoryResource, 48 | rv2.ItemFlingEffectResource, 49 | rv2.ItemPocketResource, 50 | rv2.MachineResource, 51 | rv2.MoveResource, 52 | rv2.MoveAilmentResource, 53 | rv2.MoveBattleStyleResource, 54 | rv2.MoveCategoryResource, 55 | rv2.MoveDamageClassResource, 56 | rv2.MoveLearnMethodResource, 57 | rv2.MoveTargetResource, 58 | rv2.LocationResource, 59 | rv2.LocationAreaResource, 60 | rv2.PalParkAreaResource, 61 | rv2.RegionResource, 62 | rv2.AbilityResource, 63 | rv2.CharacteristicResource, 64 | rv2.EggGroupResource, 65 | rv2.GenderResource, 66 | rv2.GrowthRateResource, 67 | rv2.NatureResource, 68 | rv2.PokeathlonStatResource, 69 | rv2.PokemonResource, 70 | rv2.PokemonColorResource, 71 | rv2.PokemonFormResource, 72 | rv2.PokemonHabitatResource, 73 | rv2.PokemonShapeResource, 74 | rv2.PokemonSpeciesResource, 75 | rv2.StatResource, 76 | rv2.TypeResource, 77 | rv2.LanguageResource 78 | ) 79 | 80 | def __init__(self, cache=None, cache_location=None, *args, **kwargs): 81 | """ 82 | Parameters 83 | ---------- 84 | cache: str 85 | cache can be 'in_memory' or 'in_disk', 86 | for memory-based or disk-based cache, respectively. 87 | Optional. 88 | cache_location: str 89 | cache directory, for disk-based cache. 90 | Optional. 91 | """ 92 | if cache is None: # empty wrapping function 93 | def no_cache(func): 94 | @functools.wraps(func) 95 | def inner(*args, **kwargs): 96 | return func(*args, **kwargs) 97 | return inner 98 | cache_function = no_cache 99 | else: 100 | if cache in ['in_memory', 'in_disk']: 101 | cache_function = self._caching(cache.split('in_')[1], cache_location) 102 | self.cache_type = cache 103 | 104 | def cache_info_total(self): 105 | return self._cache_info_(self._cache_hits_global, 106 | self._cache_misses_global, 107 | self._cache_len_global) 108 | 109 | def cache_clear_total(self): 110 | for get_method_name in self._all_get_methods_names: 111 | getattr(self, get_method_name).cache_clear() 112 | 113 | def cache_location_absolute(self): 114 | return self._cache_location_global 115 | 116 | # global cache related methods 117 | self.cache_info = types.MethodType(cache_info_total, self) 118 | self.cache_clear = types.MethodType(cache_clear_total, self) 119 | self.cache_location = types.MethodType(cache_location_absolute, self) 120 | 121 | self._cache_hits_global = 0 122 | self._cache_misses_global = 0 123 | self._cache_len_global = 0 124 | self._cache_location_global = '' 125 | self._cache_info_ = namedtuple('CacheInfo', ['hits', 'misses', 'size']) 126 | else: # wrong cache parameter 127 | raise ValueError('Accepted values for cache are "in_memory" or "in_disk"') 128 | 129 | self._cache = cache_function 130 | self._all_get_methods_names = [] 131 | super(V2Client, self).__init__(*args, **kwargs) 132 | 133 | def _assign_method(self, resource_class, method_type): 134 | """ 135 | Exactly the same code as the original except: 136 | - uid is now first parameter (after self). Therefore, no need to explicitly call 'uid=' 137 | - Ignored the other http methods besides GET (as they are not needed for the pokeapi.co API) 138 | - Added cache wrapping function 139 | - Added a way to list all get methods 140 | - Added a filter for single element lists (extract element into a standalone object) 141 | """ 142 | method_name = resource_class.get_method_name( 143 | resource_class, method_type) 144 | valid_status_codes = getattr( 145 | resource_class.Meta, 146 | 'valid_status_codes', 147 | DEFAULT_VALID_STATUS_CODES 148 | ) 149 | 150 | def extract_single_element_list(func): 151 | @functools.wraps(func) 152 | def inner(*args, **kwargs): 153 | final = func(*args, **kwargs) 154 | if len(final) == 1: 155 | final = final[0] 156 | return final 157 | return inner 158 | 159 | # uid is now the first argument (after self) 160 | @self._cache 161 | @extract_single_element_list 162 | def get(self, uid=None, method_type=method_type, 163 | method_name=method_name, 164 | valid_status_codes=valid_status_codes, 165 | resource=resource_class, data=None, **kwargs): 166 | uid = uid.lower() if isinstance(uid, str) else uid 167 | return self.call_api( 168 | method_type, method_name, 169 | valid_status_codes, resource, 170 | data, uid=uid, **kwargs) 171 | 172 | # only GET method is used 173 | setattr( 174 | self, method_name, 175 | types.MethodType(get, self) 176 | ) 177 | 178 | # for easier listing of get methods 179 | self._all_get_methods_names.append(method_name) 180 | 181 | def _caching(self, disk_or_memory, cache_directory=None): 182 | """ 183 | Decorator that allows caching the outputs of the BaseClient get methods. 184 | Cache can be either disk- or memory-based. 185 | Disk-based cache is reloaded automatically between runs if the same 186 | cache directory is specified. 187 | Cache is kept per each unique uid. 188 | 189 | ex: 190 | >> client.get_pokemon(1) -> output gets cached 191 | >> client.get_pokemon(uid=1) -> output already cached 192 | >> client.get_pokemon(2) -> output gets cached 193 | 194 | Parameters 195 | ---------- 196 | disk_or_memory: str 197 | Specify if the cache is disk- or memory-based. Accepts 'disk' or 'memory'. 198 | cache_directory: str 199 | Specify the directory for the disk-based cache. 200 | Optional, will chose an appropriate and platform-specific directory if not specified. 201 | Ignored if memory-based cache is selected. 202 | """ 203 | if disk_or_memory not in ('disk', 'memory'): 204 | raise ValueError('Accepted values are "disk" or "memory"') 205 | 206 | # Because of how BaseClient get methods are generated, they don't get a proper __name__. 207 | # As such, it is hard to generate a specific cache directory name for each get method. 208 | # Therefore, I decided to just generate a number for each folder, starting at zero. 209 | # The same get methods get the same number every time because their order doesn't change. 210 | # Also, variable is incremented inside a list because nonlocals are only python 3.0 and up. 211 | get_methods_id = [0] 212 | 213 | def memoize(func): 214 | _global_cache_dir = '' 215 | 216 | if disk_or_memory == 'disk': 217 | if cache_directory: 218 | # Python 2 and 3.4 workaround 219 | if (sys.version_info[0] == 2 and not 220 | isinstance(cache_directory, (str, unicode))) or ( 221 | sys.version_info[0:2] == (3, 4) and not 222 | isinstance(cache_directory, str)): 223 | raise TypeError('expected str, not %s' % cache_directory.__class__.__name__) 224 | 225 | _global_cache_dir = os.path.join(cache_directory, 'pokepy_cache') 226 | cache_dir = os.path.join(_global_cache_dir, str(get_methods_id[0])) 227 | else: 228 | _global_cache_dir = appdirs.user_cache_dir('pokepy_cache', False, 229 | opinion=False) 230 | cache_dir = os.path.join(_global_cache_dir, str(get_methods_id[0])) 231 | 232 | cache = FileCache('pokepy', flag='cs', app_cache_dir=cache_dir) 233 | get_methods_id[0] += 1 234 | else: # 'memory' 235 | cache = {} 236 | _global_cache_dir = 'ram' 237 | 238 | # global cache directory 239 | # should only be set when setting the first get method 240 | if not self._cache_location_global: 241 | self._cache_location_global = _global_cache_dir 242 | 243 | hits = [0] 244 | misses = [0] 245 | 246 | def cache_info(): 247 | return self._cache_info_(hits[0], misses[0], len(cache)) 248 | 249 | def cache_clear(): 250 | # global cache info 251 | self._cache_hits_global -= hits[0] 252 | self._cache_misses_global -= misses[0] 253 | self._cache_len_global -= len(cache) 254 | # local cache info 255 | hits[0] = 0 256 | misses[0] = 0 257 | 258 | cache.clear() # for disk-based cache, files are deleted but not the directories 259 | if disk_or_memory == 'disk': 260 | cache.create() # recreate cache file handles 261 | 262 | def cache_location(): 263 | return 'ram' if disk_or_memory == 'memory' else cache.cache_dir 264 | 265 | @functools.wraps(func) 266 | def memoizer(*args, **kwargs): 267 | # arguments to the get methods can be a value or uid=value 268 | key = str(args[1]) if len(args) > 1 else str(kwargs.get("uid")) 269 | 270 | if key not in cache: 271 | # local and global cache info 272 | misses[0] += 1 273 | self._cache_misses_global += 1 274 | cache[key] = func(*args, **kwargs) 275 | self._cache_len_global += 1 276 | else: 277 | self._cache_hits_global += 1 # global cache info 278 | hits[0] += 1 # local cache info 279 | return cache[key] 280 | 281 | memoizer.cache_info = cache_info 282 | memoizer.cache_clear = cache_clear 283 | memoizer.cache_location = cache_location 284 | return memoizer 285 | 286 | return memoize 287 | -------------------------------------------------------------------------------- /pokepy/fcache/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | fcache 6 | 7 | Same codebase as fcache 0.4.7 (https://pypi.org/project/fcache/) 8 | except for a single fix (https://github.com/tsroten/fcache/pull/29) 9 | """ 10 | -------------------------------------------------------------------------------- /pokepy/fcache/cache.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import logging 3 | import os 4 | import pickle 5 | import shutil 6 | import tempfile 7 | 8 | import appdirs 9 | 10 | try: 11 | from collections.abc import MutableMapping 12 | unicode = str 13 | except ImportError: 14 | # Python 2 imports 15 | from collections import MutableMapping 16 | FileNotFoundError = IOError 17 | 18 | from .posixemulation import rename 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | 23 | class FileCache(MutableMapping): 24 | """A persistent file cache that is dictionary-like and has a write buffer. 25 | 26 | *appname* is passed to `appdirs `_ 27 | to determine a system-appropriate location for the cache files. The cache 28 | directory used is available via :data:`cache_dir`. 29 | 30 | By default, a write buffer is used, so writing to cache files is not done 31 | until :meth:`sync` is explicitly called. This behavior can be changed using 32 | the optional *flag* argument. 33 | 34 | .. NOTE:: 35 | Keys and values are always stored as :class:`bytes` objects. If data 36 | serialization is enabled, keys are returned as :class:`str` or 37 | :class:`unicode` objects. 38 | If data serialization is disabled, keys are returned as a 39 | :class:`bytes` object. 40 | 41 | :param str appname: The app/script the cache should be associated with. 42 | :param str flag: How the cache should be opened. See below for details. 43 | :param mode: The Unix mode for the cache files. 44 | :param str keyencoding: The encoding the keys use, defaults to 'utf-8'. 45 | This is used if *serialize* is ``False``; the keys are treated as 46 | :class:`bytes` objects. 47 | :param bool serialize: Whether or not to (de)serialize the values. If a 48 | cache is used with a :class:`~shelve.Shelf`, set this to ``False``. 49 | :param str app_cache_dir: absolute path to root cache directory to be 50 | used in place of system-appropriate location determined by appdirs 51 | 52 | The optional *flag* argument can be: 53 | 54 | +---------+-------------------------------------------+ 55 | | Value | Meaning | 56 | +=========+===========================================+ 57 | | ``'r'`` | Open existing cache for reading only | 58 | +---------+-------------------------------------------+ 59 | | ``'w'`` | Open existing cache for reading and | 60 | | | writing | 61 | +---------+-------------------------------------------+ 62 | | ``'c'`` | Open cache for reading and writing, | 63 | | | creating it if it doesn't exist (default) | 64 | +---------+-------------------------------------------+ 65 | | ``'n'`` | Always create a new, empty cache, open | 66 | | | for reading and writing | 67 | +---------+-------------------------------------------+ 68 | 69 | If a ``'s'`` is appended to the *flag* argument, the cache will be opened 70 | in sync mode. Writing to the cache will happen immediately and will not be 71 | buffered. 72 | 73 | If an application needs to use more than one cache, then it should use 74 | subcaches. To create a subcache, append a series of one or more names 75 | separated by periods to the application name when creating a 76 | :class:`FileCache` object (e.g. ``'appname.subcache'`` or 77 | ``'appname.subcache.subcache'``). 78 | Subcaches are a way for an application to use more than one cache without 79 | polluting a user's cache directory. All caches -- main caches or subcaches 80 | -- are totally independent. The only aspect in which they are linked is 81 | that all of an application's caches exist in the same system directory. 82 | Because each cache is independent of every other cache, calling 83 | :meth:`delete` on an application's main cache will not delete data in 84 | its subcaches. 85 | 86 | """ 87 | 88 | def __init__(self, appname, flag='c', mode=0o666, keyencoding='utf-8', 89 | serialize=True, app_cache_dir=None): 90 | """Initialize a :class:`FileCache` object.""" 91 | if not isinstance(flag, str): 92 | raise TypeError("flag must be str not '{}'".format(type(flag))) 93 | elif flag[0] not in 'rwcn': 94 | raise ValueError("invalid flag: '{}', first flag must be one of " 95 | "'r', 'w', 'c' or 'n'".format(flag)) 96 | elif len(flag) > 1 and flag[1] != 's': 97 | raise ValueError("invalid flag: '{}', second flag must be " 98 | "'s'".format(flag)) 99 | 100 | appname, subcache = self._parse_appname(appname) 101 | if 'cache' in subcache: 102 | raise ValueError("invalid subcache name: 'cache'.") 103 | self._is_subcache = bool(subcache) 104 | 105 | if not app_cache_dir: 106 | app_cache_dir = appdirs.user_cache_dir(appname, appname) 107 | subcache_dir = os.path.join(app_cache_dir, *subcache) 108 | self.cache_dir = os.path.join(subcache_dir, 'cache') 109 | exists = os.path.exists(self.cache_dir) 110 | 111 | if len(flag) > 1 and flag[1] == 's': 112 | self._sync = True 113 | else: 114 | self._sync = False 115 | self._buffer = {} 116 | 117 | if exists and 'n' in flag: 118 | self.clear() 119 | self.create() 120 | elif not exists and ('c' in flag or 'n' in flag): 121 | self.create() 122 | elif not exists: 123 | raise FileNotFoundError("no such directory: '{}'".format( 124 | self.cache_dir)) 125 | 126 | self._flag = 'rb' if 'r' in flag else 'wb' 127 | self._mode = mode 128 | self._keyencoding = keyencoding 129 | self._serialize = serialize 130 | 131 | def _parse_appname(self, appname): 132 | """Splits an appname into the appname and subcache components.""" 133 | components = appname.split('.') 134 | return components[0], components[1:] 135 | 136 | def create(self): 137 | """Create the write buffer and cache directory.""" 138 | if not self._sync and not hasattr(self, '_buffer'): 139 | self._buffer = {} 140 | if not os.path.exists(self.cache_dir): 141 | os.makedirs(self.cache_dir) 142 | 143 | def clear(self): 144 | """Remove all items from the write buffer and cache. 145 | 146 | The write buffer object and cache directory are not deleted. 147 | 148 | """ 149 | self.delete() 150 | self.create() 151 | 152 | def delete(self): 153 | """Delete the write buffer and cache directory.""" 154 | if not self._sync: 155 | del self._buffer 156 | shutil.rmtree(self.cache_dir) 157 | 158 | def close(self): 159 | """Sync the write buffer, then close the cache. 160 | 161 | If a closed :class:`FileCache` object's methods are called, a 162 | :exc:`ValueError` will be raised. 163 | 164 | """ 165 | self.sync() 166 | self.sync = self.create = self.delete = self._closed 167 | self._write_to_file = self._read_to_file = self._closed 168 | self._key_to_filename = self._filename_to_key = self._closed 169 | self.__getitem__ = self.__setitem__ = self.__delitem__ = self._closed 170 | self.__iter__ = self.__len__ = self.__contains__ = self._closed 171 | 172 | def sync(self): 173 | """Sync the write buffer with the cache files and clear the buffer. 174 | 175 | If the :class:`FileCache` object was opened with the optional ``'s'`` 176 | *flag* argument, then calling :meth:`sync` will do nothing. 177 | """ 178 | if self._sync: 179 | return # opened in sync mode, so skip the manual sync 180 | self._sync = True 181 | for ekey in self._buffer: 182 | filename = self._key_to_filename(ekey) 183 | self._write_to_file(filename, self._buffer[ekey]) 184 | self._buffer.clear() 185 | self._sync = False 186 | 187 | def _closed(self, *args, **kwargs): 188 | """Filler method for closed cache methods.""" 189 | raise ValueError("invalid operation on closed cache") 190 | 191 | def _encode_key(self, key): 192 | """Encode key using *hex_codec* for constructing a cache filename. 193 | 194 | Keys are implicitly converted to :class:`bytes` if passed as 195 | :class:`str`. 196 | 197 | """ 198 | if isinstance(key, str) or isinstance(key, unicode): 199 | key = key.encode(self._keyencoding) 200 | elif not isinstance(key, bytes): 201 | raise TypeError("key must be bytes or str") 202 | return codecs.encode(key, 'hex_codec').decode(self._keyencoding) 203 | 204 | def _decode_key(self, key): 205 | """Decode key using hex_codec to retrieve the original key. 206 | 207 | Keys are returned as :class:`str` if serialization is enabled. 208 | Keys are returned as :class:`bytes` if serialization is disabled. 209 | 210 | """ 211 | bkey = codecs.decode(key.encode(self._keyencoding), 'hex_codec') 212 | return bkey.decode(self._keyencoding) if self._serialize else bkey 213 | 214 | def _dumps(self, value): 215 | return value if not self._serialize else pickle.dumps(value) 216 | 217 | def _loads(self, value): 218 | return value if not self._serialize else pickle.loads(value) 219 | 220 | def _key_to_filename(self, key): 221 | """Convert an encoded key to an absolute cache filename.""" 222 | return os.path.join(self.cache_dir, key) 223 | 224 | def _filename_to_key(self, absfilename): 225 | """Convert an absolute cache filename to a key name.""" 226 | return os.path.split(absfilename)[1] 227 | 228 | def _all_filenames(self): 229 | """Return a list of absolute cache filenames""" 230 | try: 231 | return [os.path.join(self.cache_dir, filename) for filename in 232 | os.listdir(self.cache_dir)] 233 | except (FileNotFoundError, OSError): 234 | return [] 235 | 236 | def _all_keys(self): 237 | """Return a list of all encoded key names.""" 238 | file_keys = [self._filename_to_key(fn) for fn in self._all_filenames()] 239 | if self._sync: 240 | return set(file_keys) 241 | else: 242 | return set(file_keys + list(self._buffer)) 243 | 244 | def _write_to_file(self, filename, bytesvalue): 245 | """Write bytesvalue to filename.""" 246 | fh, tmp = tempfile.mkstemp() 247 | with os.fdopen(fh, self._flag) as f: 248 | f.write(self._dumps(bytesvalue)) 249 | rename(tmp, filename) 250 | os.chmod(filename, self._mode) 251 | 252 | def _read_from_file(self, filename): 253 | """Read data from filename.""" 254 | try: 255 | with open(filename, 'rb') as f: 256 | return self._loads(f.read()) 257 | except (IOError, OSError): 258 | logger.warning('Error opening file: {}'.format(filename)) 259 | return None 260 | 261 | def __setitem__(self, key, value): 262 | ekey = self._encode_key(key) 263 | if not self._sync: 264 | self._buffer[ekey] = value 265 | else: 266 | filename = self._key_to_filename(ekey) 267 | self._write_to_file(filename, value) 268 | 269 | def __getitem__(self, key): 270 | ekey = self._encode_key(key) 271 | if not self._sync: 272 | try: 273 | return self._buffer[ekey] 274 | except KeyError: 275 | pass 276 | filename = self._key_to_filename(ekey) 277 | if filename not in self._all_filenames(): 278 | raise KeyError(key) 279 | return self._read_from_file(filename) 280 | 281 | def __delitem__(self, key): 282 | ekey = self._encode_key(key) 283 | filename = self._key_to_filename(ekey) 284 | if not self._sync: 285 | try: 286 | del self._buffer[ekey] 287 | except KeyError: 288 | if filename not in self._all_filenames(): 289 | raise KeyError(key) 290 | try: 291 | os.remove(filename) 292 | except (IOError, OSError): 293 | pass 294 | 295 | def __iter__(self): 296 | for key in self._all_keys(): 297 | yield self._decode_key(key) 298 | 299 | def __len__(self): 300 | return len(self._all_keys()) 301 | 302 | def __contains__(self, key): 303 | ekey = self._encode_key(key) 304 | return ekey in self._all_keys() 305 | -------------------------------------------------------------------------------- /pokepy/fcache/posixemulation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r""" 3 | werkzeug.posixemulation 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Provides a POSIX emulation for some features that are relevant to 7 | web applications. The main purpose is to simplify support for 8 | systems such as Windows NT that are not 100% POSIX compatible. 9 | 10 | Currently this only implements a :func:`rename` function that 11 | follows POSIX semantics. Eg: if the target file already exists it 12 | will be replaced without asking. 13 | 14 | This module was introduced in 0.6.1 and is not a public interface. 15 | It might become one in later versions of Werkzeug. 16 | 17 | :copyright: (c) 2013 by the Werkzeug Team, see AUTHORS for more details. 18 | :license: BSD, see LICENSE for more details. 19 | """ 20 | import sys 21 | import os 22 | import errno 23 | import time 24 | import random 25 | import shutil 26 | 27 | 28 | can_rename_open_file = False 29 | if os.name == 'nt': # pragma: no cover 30 | _rename = lambda src, dst: False 31 | _rename_atomic = lambda src, dst: False 32 | if sys.version_info >= (3, 0): 33 | unicode = str 34 | 35 | try: 36 | import ctypes 37 | 38 | _MOVEFILE_REPLACE_EXISTING = 0x1 39 | _MOVEFILE_WRITE_THROUGH = 0x8 40 | _MoveFileEx = ctypes.windll.kernel32.MoveFileExW 41 | 42 | def _rename(src, dst): 43 | if not isinstance(src, unicode): 44 | src = unicode(src, sys.getfilesystemencoding()) 45 | if not isinstance(dst, unicode): 46 | dst = unicode(dst, sys.getfilesystemencoding()) 47 | if _rename_atomic(src, dst): 48 | return True 49 | retry = 0 50 | rv = False 51 | while not rv and retry < 100: 52 | rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING | 53 | _MOVEFILE_WRITE_THROUGH) 54 | if not rv: 55 | time.sleep(0.001) 56 | retry += 1 57 | return rv 58 | 59 | # new in Vista and Windows Server 2008 60 | _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction 61 | _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction 62 | _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW 63 | _CloseHandle = ctypes.windll.kernel32.CloseHandle 64 | can_rename_open_file = True 65 | 66 | def _rename_atomic(src, dst): 67 | ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename') 68 | if ta == -1: 69 | return False 70 | try: 71 | retry = 0 72 | rv = False 73 | while not rv and retry < 100: 74 | rv = _MoveFileTransacted(src, dst, None, None, 75 | _MOVEFILE_REPLACE_EXISTING | 76 | _MOVEFILE_WRITE_THROUGH, ta) 77 | if rv: 78 | rv = _CommitTransaction(ta) 79 | break 80 | else: 81 | time.sleep(0.001) 82 | retry += 1 83 | return rv 84 | finally: 85 | _CloseHandle(ta) 86 | except Exception: 87 | pass 88 | 89 | def rename(src, dst): 90 | # Try atomic or pseudo-atomic rename 91 | if _rename(src, dst): 92 | return 93 | # Fall back to "move away and replace" 94 | try: 95 | shutil.move(src, dst) 96 | except OSError as e: 97 | if e.errno != errno.EEXIST: 98 | raise 99 | old = "%s-%08x" % (dst, random.randint(0, sys.maxint)) 100 | shutil.move(dst, old) 101 | shutil.move(src, dst) 102 | try: 103 | os.unlink(old) 104 | except Exception: 105 | pass 106 | else: 107 | """ 108 | If dst on current filesystem then use 109 | atomic rename. Otherwise, fall back to a 110 | non-atomic copy and remove. 111 | """ 112 | rename = shutil.move 113 | can_rename_open_file = True 114 | -------------------------------------------------------------------------------- /pokepy/resources_v2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | Resources related to the V2 API 6 | 7 | Refer to the documentation for more information 8 | (https://pokeapi.co/docs/v2.html) 9 | """ 10 | 11 | from beckett.resources import BaseResource as OriginalBaseResource 12 | from beckett.resources import SubResource 13 | 14 | 15 | class BaseResource(OriginalBaseResource): 16 | """ 17 | Fix for "type object argument after ** must be a mapping, not NoneType" 18 | """ 19 | def set_subresources(self, **kwargs): 20 | """Same logic as the original except for the first 'if' clause.""" 21 | for attribute_name, resource in self._subresource_map.items(): 22 | sub_attr = kwargs.get(attribute_name) 23 | if sub_attr is None: 24 | # Attribute was not found or is null 25 | value = None 26 | elif isinstance(sub_attr, list): 27 | # A list of subresources is supported 28 | value = [resource(**x) for x in sub_attr] 29 | else: 30 | # So is a single resource 31 | value = resource(**sub_attr) 32 | setattr(self, attribute_name, value) 33 | 34 | 35 | ############################## 36 | # Common Models (SubResources) 37 | ############################## 38 | 39 | 40 | class APIResourceSubResource(SubResource): 41 | class Meta: 42 | name = 'API_Resource' 43 | identifier = 'url' 44 | attributes = ( 45 | 'url', 46 | ) 47 | 48 | def __repr__(self): 49 | return '<%s - %s>' % (self.Meta.name, self.url) 50 | 51 | 52 | class NamedAPIResourceSubResource(SubResource): 53 | class Meta: 54 | name = 'Named_API_Resource' 55 | identifier = 'name' 56 | attributes = ( 57 | 'name', 58 | 'url' 59 | ) 60 | 61 | def __repr__(self): 62 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 63 | 64 | 65 | class DescriptionSubResource(BaseResource): 66 | class Meta(BaseResource.Meta): 67 | name = 'Description' 68 | identifier = 'description' 69 | attributes = ( 70 | 'description', 71 | ) 72 | subresources = { 73 | 'language': NamedAPIResourceSubResource 74 | } 75 | 76 | def __repr__(self): 77 | return '<%s - %s>' % (self.Meta.name, 78 | self.description.capitalize()[:10] + "...") 79 | 80 | 81 | class EffectSubResource(BaseResource): 82 | class Meta(BaseResource.Meta): 83 | name = 'Effect' 84 | identifier = 'effect' 85 | attributes = ( 86 | 'effect', 87 | ) 88 | subresources = { 89 | 'language': NamedAPIResourceSubResource 90 | } 91 | 92 | def __repr__(self): 93 | return '<%s - %s>' % (self.Meta.name, 94 | self.effect.capitalize()[:10] + "...") 95 | 96 | 97 | class EncounterSubResource(BaseResource): 98 | class Meta(BaseResource.Meta): 99 | name = 'Encounter' 100 | identifier = 'chance' 101 | attributes = ( 102 | 'min_level', 103 | 'max_level', 104 | 'chance' 105 | ) 106 | subresources = { 107 | 'condition_values': NamedAPIResourceSubResource, 108 | 'method': NamedAPIResourceSubResource 109 | } 110 | 111 | def __repr__(self): 112 | return '<%s - %s/%s/%s>' % (self.Meta.name, self.min_level, 113 | self.max_level, self.chance) 114 | 115 | 116 | class FlavorTextSubResource(BaseResource): 117 | class Meta(BaseResource.Meta): 118 | name = 'Flavor_Text' 119 | identifier = 'flavor_text' 120 | attributes = ( 121 | 'flavor_text', 122 | ) 123 | subresources = { 124 | 'language': NamedAPIResourceSubResource, 125 | 'version': NamedAPIResourceSubResource 126 | } 127 | 128 | def __repr__(self): 129 | return '<%s - %s>' % (self.Meta.name, 130 | self.flavor_text.capitalize()[:10] + "...") 131 | 132 | 133 | class GenerationGameIndexSubResource(BaseResource): 134 | class Meta(BaseResource.Meta): 135 | name = 'Generation_Game_Index' 136 | identifier = 'game_index' 137 | attributes = ( 138 | 'game_index', 139 | ) 140 | subresources = { 141 | 'generation': NamedAPIResourceSubResource 142 | } 143 | 144 | def __repr__(self): 145 | return '<%s - %s>' % (self.Meta.name, self.game_index) 146 | 147 | 148 | class MachineVersionDetailSubResource(BaseResource): 149 | class Meta(BaseResource.Meta): 150 | name = 'Machine_Version_Detail' 151 | identifier = 'machine' 152 | subresources = { 153 | 'machine': APIResourceSubResource, 154 | 'version_group': NamedAPIResourceSubResource 155 | } 156 | 157 | def __repr__(self): 158 | return '<%s>' % self.Meta.name 159 | 160 | 161 | class NameSubResource(BaseResource): 162 | class Meta: 163 | name = 'Name' 164 | identifier = 'name' 165 | attributes = ( 166 | 'name', 167 | ) 168 | subresources = { 169 | 'language': NamedAPIResourceSubResource 170 | } 171 | 172 | def __repr__(self): 173 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 174 | 175 | 176 | class VerboseEffectSubResource(BaseResource): 177 | class Meta(BaseResource.Meta): 178 | name = 'Verbose_Effect' 179 | identifier = 'effect' 180 | attributes = ( 181 | 'effect', 182 | 'short_effect' 183 | ) 184 | subresources = { 185 | 'language': NamedAPIResourceSubResource 186 | } 187 | 188 | def __repr__(self): 189 | return '<%s - %s>' % (self.Meta.name, self.effect.capitalize()) 190 | 191 | 192 | class VersionEncounterDetailSubResource(BaseResource): 193 | class Meta(BaseResource.Meta): 194 | name = 'Version_Encounter_Detail' 195 | identifier = 'max_chance' 196 | attributes = ( 197 | 'max_chance', 198 | ) 199 | subresources = { 200 | 'version': NamedAPIResourceSubResource, 201 | 'encounter_details': EncounterSubResource 202 | } 203 | 204 | def __repr__(self): 205 | return '<%s - %s>' % (self.Meta.name, self.max_chance) 206 | 207 | 208 | class VersionGameIndexSubResource(BaseResource): 209 | class Meta(BaseResource.Meta): 210 | name = 'Version_Game_Index' 211 | identifier = 'game_index' 212 | attributes = ( 213 | 'game_index', 214 | ) 215 | subresources = { 216 | 'version': NamedAPIResourceSubResource 217 | } 218 | 219 | def __repr__(self): 220 | return '<%s - %s>' % (self.Meta.name, self.game_index) 221 | 222 | 223 | class VersionGroupFlavorTextSubResource(BaseResource): 224 | class Meta(BaseResource.Meta): 225 | name = 'Version_Group_Flavor_Text' 226 | identifier = 'text' 227 | attributes = ( 228 | 'text', 229 | ) 230 | subresources = { 231 | 'language': NamedAPIResourceSubResource, 232 | 'version_group': NamedAPIResourceSubResource 233 | } 234 | 235 | def __repr__(self): 236 | return '<%s - %s>' % (self.Meta.name, 237 | self.text.capitalize()[:10] + "...") 238 | 239 | 240 | ############## 241 | # SubResources 242 | ############## 243 | 244 | 245 | class BerryFlavorMapSubResource(BaseResource): 246 | class Meta(BaseResource.Meta): 247 | name = 'Berry_Flavor_Map' 248 | identifier = 'potency' 249 | attributes = ( 250 | 'potency', 251 | ) 252 | subresources = { 253 | 'flavor': NamedAPIResourceSubResource 254 | } 255 | 256 | def __repr__(self): 257 | return '<%s - %s>' % (self.Meta.name, self.potency) 258 | 259 | 260 | class FlavorBerryMapSubResource(BaseResource): 261 | class Meta(BaseResource.Meta): 262 | name = 'Flavor_Berry_Map' 263 | identifier = 'potency' 264 | attributes = ( 265 | 'potency', 266 | ) 267 | subresources = { 268 | 'berry': NamedAPIResourceSubResource 269 | } 270 | 271 | def __repr__(self): 272 | return '<%s - %s>' % (self.Meta.name, self.potency) 273 | 274 | 275 | class ContestNameSubResource(BaseResource): 276 | class Meta(BaseResource.Meta): 277 | name = 'Contest_Name' 278 | identifier = 'name' 279 | attributes = ( 280 | 'name', 281 | 'color' 282 | ) 283 | subresources = { 284 | 'language': NamedAPIResourceSubResource 285 | } 286 | 287 | def __repr__(self): 288 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 289 | 290 | 291 | class EvolutionDetailSubResource(BaseResource): 292 | class Meta(BaseResource.Meta): 293 | name = 'Evolution_Detail' 294 | identifier = 'gender' 295 | attributes = ( 296 | 'gender', 297 | 'min_level', 298 | 'min_happiness', 299 | 'min_beauty', 300 | 'min_affection', 301 | 'needs_overworld_rain', 302 | 'relative_physical_stats', 303 | 'time_of_day', 304 | 'turn_upside_down' 305 | ) 306 | subresources = { 307 | 'item': NamedAPIResourceSubResource, 308 | 'trigger': NamedAPIResourceSubResource, 309 | 'held_item': NamedAPIResourceSubResource, 310 | 'known_move': NamedAPIResourceSubResource, 311 | 'known_move_type': NamedAPIResourceSubResource, 312 | 'location': NamedAPIResourceSubResource, 313 | 'party_species': NamedAPIResourceSubResource, 314 | 'party_type': NamedAPIResourceSubResource, 315 | 'trade_species': NamedAPIResourceSubResource 316 | } 317 | 318 | def __repr__(self): 319 | return '<%s>' % self.Meta.name 320 | 321 | 322 | # A ChainLink is tricky to implement with beckett because it is recursive. 323 | # The current solution is to have 3 nearly identical ChainLink classes 324 | # (so far, the maximum number of chaining evolutions depth is 3) 325 | # the last one not having the evolves_to subresource, ending the recursion. 326 | 327 | 328 | class ChainLink2SubResource(BaseResource): 329 | class Meta(BaseResource.Meta): 330 | name = 'Chain_Link2' 331 | identifier = 'is_baby' 332 | attributes = ( 333 | 'is_baby', 334 | ) 335 | subresources = { 336 | 'species': NamedAPIResourceSubResource, 337 | 'evolution_details': EvolutionDetailSubResource 338 | } 339 | 340 | def __repr__(self): 341 | return '<%s>' % self.Meta.name 342 | 343 | 344 | class ChainLink1SubResource(BaseResource): 345 | class Meta(BaseResource.Meta): 346 | name = 'Chain_Link1' 347 | identifier = 'is_baby' 348 | attributes = ( 349 | 'is_baby', 350 | ) 351 | subresources = { 352 | 'species': NamedAPIResourceSubResource, 353 | 'evolution_details': EvolutionDetailSubResource, 354 | 'evolves_to': ChainLink2SubResource 355 | } 356 | 357 | def __repr__(self): 358 | return '<%s>' % self.Meta.name 359 | 360 | 361 | class ChainLinkSubResource(BaseResource): 362 | class Meta(BaseResource.Meta): 363 | name = 'Chain_Link' 364 | identifier = 'is_baby' 365 | attributes = ( 366 | 'is_baby', 367 | ) 368 | subresources = { 369 | 'species': NamedAPIResourceSubResource, 370 | 'evolution_details': EvolutionDetailSubResource, 371 | 'evolves_to': ChainLink1SubResource 372 | } 373 | 374 | def __repr__(self): 375 | return '<%s>' % self.Meta.name 376 | 377 | 378 | class PokemonEntrySubResource(BaseResource): 379 | class Meta(BaseResource.Meta): 380 | name = 'Pokemon_Entry' 381 | identifier = 'entry_number' 382 | attributes = ( 383 | 'entry_number', 384 | ) 385 | subresources = { 386 | 'pokemon_species': NamedAPIResourceSubResource 387 | } 388 | 389 | def __repr__(self): 390 | return '<%s - %s>' % (self.Meta.name, self.entry_number) 391 | 392 | 393 | class ItemSpritesSubResource(SubResource): 394 | class Meta(BaseResource.Meta): 395 | name = 'Item_Sprites' 396 | identifier = 'default' 397 | attributes = ( 398 | 'default', 399 | ) 400 | 401 | def __repr__(self): 402 | return '<%s - %s>' % (self.Meta.name, 403 | self.default.capitalize()[:10] + "...") 404 | 405 | 406 | class ItemHolderPokemonVersionDetailSubResource(BaseResource): 407 | class Meta(BaseResource.Meta): 408 | name = 'Item_Holder_Pokemon_Version_Detail' 409 | identifier = 'rarity' 410 | attributes = ( 411 | 'rarity', 412 | ) 413 | subresources = { 414 | 'version': NamedAPIResourceSubResource 415 | } 416 | 417 | def __repr__(self): 418 | return '<%s>' % self.Meta.name 419 | 420 | 421 | class ItemHolderPokemonSubResource(BaseResource): 422 | class Meta(BaseResource.Meta): 423 | name = 'Item_Holder_Pokemon' 424 | identifier = 'pokemon' 425 | subresources = { 426 | 'pokemon': NamedAPIResourceSubResource, 427 | 'version_details': ItemHolderPokemonVersionDetailSubResource 428 | } 429 | 430 | def __repr__(self): 431 | return '<%s>' % self.Meta.name 432 | 433 | 434 | class ContestComboDetailSubResource(BaseResource): 435 | class Meta(BaseResource.Meta): 436 | name = 'Contest_Combo_Detail' 437 | identifier = 'use_before' 438 | subresources = { 439 | 'use_before': NamedAPIResourceSubResource, 440 | 'use_after': NamedAPIResourceSubResource 441 | } 442 | 443 | def __repr__(self): 444 | return '<%s>' % self.Meta.name 445 | 446 | 447 | class ContestComboSetsSubResource(BaseResource): 448 | class Meta(BaseResource.Meta): 449 | name = 'Contest_Combo_Sets' 450 | identifier = 'normal' 451 | subresources = { 452 | 'normal': ContestComboDetailSubResource, 453 | 'super': ContestComboDetailSubResource 454 | } 455 | 456 | def __repr__(self): 457 | return '<%s>' % self.Meta.name 458 | 459 | 460 | class MoveFlavorTextSubResource(BaseResource): 461 | class Meta(BaseResource.Meta): 462 | name = 'Move_Flavor_Text' 463 | identifier = 'flavor_text' 464 | attributes = ( 465 | 'flavor_text', 466 | ) 467 | subresources = { 468 | 'language': NamedAPIResourceSubResource, 469 | 'version_group': NamedAPIResourceSubResource 470 | } 471 | 472 | def __repr__(self): 473 | return '<%s - %s>' % (self.Meta.name, 474 | self.flavor_text.capitalize()[:10] + "...") 475 | 476 | 477 | class MoveMetaDataSubResource(BaseResource): 478 | class Meta(BaseResource.Meta): 479 | name = 'Move_Meta_Data' 480 | identifier = 'min_hits' 481 | attributes = ( 482 | 'min_hits', 483 | 'max_hits', 484 | 'min_turns', 485 | 'max_turns', 486 | 'drain', 487 | 'healing', 488 | 'crit_rate', 489 | 'ailment_chance', 490 | 'flinch_chance', 491 | 'stat_chance' 492 | ) 493 | subresources = { 494 | 'ailment': NamedAPIResourceSubResource, 495 | 'category': NamedAPIResourceSubResource 496 | } 497 | 498 | def __repr__(self): 499 | return '<%s>' % self.Meta.name 500 | 501 | 502 | class MoveStatChangeSubResource(BaseResource): 503 | class Meta(BaseResource.Meta): 504 | name = 'Move_Stat_Change' 505 | identifier = 'change' 506 | attributes = ( 507 | 'change', 508 | ) 509 | subresources = { 510 | 'stat': NamedAPIResourceSubResource 511 | } 512 | 513 | def __repr__(self): 514 | return '<%s - %s>' % (self.Meta.name, self.change) 515 | 516 | 517 | class PastMoveStatValuesSubResource(BaseResource): 518 | class Meta(BaseResource.Meta): 519 | name = 'Past_Move_Stat_Values' 520 | identifier = 'accuracy' 521 | attributes = ( 522 | 'accuracy', 523 | 'effect_chance', 524 | 'power', 525 | 'pp' 526 | ) 527 | subresources = { 528 | 'effect_entries': VerboseEffectSubResource, 529 | 'type': NamedAPIResourceSubResource, 530 | 'version_group': NamedAPIResourceSubResource 531 | } 532 | 533 | def __repr__(self): 534 | return '<%s>' % self.Meta.name 535 | 536 | 537 | class EncounterVersionDetailsSubResource(BaseResource): 538 | class Meta(BaseResource.Meta): 539 | name = 'Encounter_Version_Details' 540 | identifier = 'rate' 541 | attributes = ( 542 | 'rate', 543 | ) 544 | subresources = { 545 | 'version': NamedAPIResourceSubResource 546 | } 547 | 548 | def __repr__(self): 549 | return '<%s - %s>' % (self.Meta.name, self.rate) 550 | 551 | 552 | class EncounterMethodRateSubResource(BaseResource): 553 | class Meta(BaseResource.Meta): 554 | name = 'Encounter_Method_Rate' 555 | identifier = 'encounter_method' 556 | subresources = { 557 | 'encounter_method': NamedAPIResourceSubResource, 558 | 'version_details': EncounterVersionDetailsSubResource 559 | } 560 | 561 | def __repr__(self): 562 | return '<%s>' % self.Meta.name 563 | 564 | 565 | class PokemonEncounterSubResource(BaseResource): 566 | class Meta(BaseResource.Meta): 567 | name = 'Pokemon_Encounter' 568 | identifier = 'pokemon' 569 | subresources = { 570 | 'pokemon': NamedAPIResourceSubResource, 571 | 'version_details': VersionEncounterDetailSubResource 572 | } 573 | 574 | def __repr__(self): 575 | return '<%s>' % self.Meta.name 576 | 577 | 578 | class PalParkEncounterSpeciesSubResource(BaseResource): 579 | class Meta(BaseResource.Meta): 580 | name = 'Pal_Park_Encounter_Species' 581 | identifier = 'rate' 582 | attributes = ( 583 | 'base_score', 584 | 'rate' 585 | ) 586 | subresources = { 587 | 'pokemon_species': NamedAPIResourceSubResource 588 | } 589 | 590 | def __repr__(self): 591 | return '<%s>' % self.Meta.name 592 | 593 | 594 | class AbilityEffectChangeSubResource(BaseResource): 595 | class Meta(BaseResource.Meta): 596 | name = 'Ability_Effect_Change' 597 | identifier = 'effect_entries' 598 | subresources = { 599 | 'effect_entries': EffectSubResource, 600 | 'version_group': NamedAPIResourceSubResource 601 | } 602 | 603 | def __repr__(self): 604 | return '<%s>' % self.Meta.name 605 | 606 | 607 | class AbilityFlavorTextSubResource(BaseResource): 608 | class Meta(BaseResource.Meta): 609 | name = 'Ability_Flavor_Text' 610 | identifier = 'flavor_text' 611 | attributes = ( 612 | 'flavor_text', 613 | ) 614 | subresources = { 615 | 'language': NamedAPIResourceSubResource, 616 | 'version_group': NamedAPIResourceSubResource 617 | } 618 | 619 | def __repr__(self): 620 | return '<%s - %s>' % (self.Meta.name, 621 | self.flavor_text.capitalize()[:10] + "...") 622 | 623 | 624 | class AbilityPokemonSubResource(BaseResource): 625 | class Meta(BaseResource.Meta): 626 | name = 'Ability_Pokemon' 627 | identifier = 'slot' 628 | attributes = ( 629 | 'is_hidden', 630 | 'slot' 631 | ) 632 | subresources = { 633 | 'pokemon': NamedAPIResourceSubResource 634 | } 635 | 636 | def __repr__(self): 637 | return '<%s>' % self.Meta.name 638 | 639 | 640 | class PokemonSpeciesGenderSubResource(BaseResource): 641 | class Meta(BaseResource.Meta): 642 | name = 'Pokemon_Species_Gender' 643 | identifier = 'rate' 644 | attributes = ( 645 | 'rate', 646 | ) 647 | subresources = { 648 | 'pokemon_species': NamedAPIResourceSubResource 649 | } 650 | 651 | def __repr__(self): 652 | return '<%s - %s>' % (self.Meta.name, self.rate) 653 | 654 | 655 | class GrowthRateExperienceLevelSubResource(SubResource): 656 | class Meta(BaseResource.Meta): 657 | name = 'Growth_Rate_Experience_Level' 658 | identifier = 'level' 659 | attributes = ( 660 | 'level', 661 | 'experience' 662 | ) 663 | 664 | def __repr__(self): 665 | return '<%s - %s/%s>' % (self.Meta.name, self.level, self.experience) 666 | 667 | 668 | class NatureStatChangeSubResource(BaseResource): 669 | class Meta(BaseResource.Meta): 670 | name = 'Nature_Stat_Change' 671 | identifier = 'max_change' 672 | attributes = ( 673 | 'max_change', 674 | ) 675 | subresources = { 676 | 'pokeathlon_stat': NamedAPIResourceSubResource 677 | } 678 | 679 | def __repr__(self): 680 | return '<%s - %s>' % (self.Meta.name, self.max_change) 681 | 682 | 683 | class MoveBattleStylePreferenceSubResource(BaseResource): 684 | class Meta(BaseResource.Meta): 685 | name = 'Move_Battle_Style_Preference' 686 | identifier = 'low_hp_preference' 687 | attributes = ( 688 | 'low_hp_preference', 689 | 'high_hp_preference' 690 | ) 691 | subresources = { 692 | 'move_battle_style': NamedAPIResourceSubResource 693 | } 694 | 695 | def __repr__(self): 696 | return '<%s - %s/%s>' % (self.Meta.name, self.low_hp_preference, 697 | self.high_hp_preference) 698 | 699 | 700 | class NaturePokeathlonStatAffectSubResource(BaseResource): 701 | class Meta(BaseResource.Meta): 702 | name = 'Nature_Pokeathlon_Stat_Affect' 703 | identifier = 'max_change' 704 | attributes = ( 705 | 'max_change', 706 | ) 707 | subresources = { 708 | 'nature': NamedAPIResourceSubResource 709 | } 710 | 711 | def __repr__(self): 712 | return '<%s - %s>' % (self.Meta.name, self.max_change) 713 | 714 | 715 | class NaturePokeathlonStatAffectSetsSubResource(BaseResource): 716 | class Meta(BaseResource.Meta): 717 | name = 'Nature_Pokeathlon_Stat_Affect_Sets' 718 | identifier = 'increase' 719 | subresources = { 720 | 'increase': NaturePokeathlonStatAffectSubResource, 721 | 'decrease': NaturePokeathlonStatAffectSubResource 722 | } 723 | 724 | def __repr__(self): 725 | return '<%s>' % self.Meta.name 726 | 727 | 728 | class PokemonAbilitySubResource(BaseResource): 729 | class Meta(BaseResource.Meta): 730 | name = 'Pokemon_Ability' 731 | identifier = 'is_hidden' 732 | attributes = ( 733 | 'is_hidden', 734 | 'slot' 735 | ) 736 | subresources = { 737 | 'ability': NamedAPIResourceSubResource 738 | } 739 | 740 | def __repr__(self): 741 | return '<%s>' % self.Meta.name 742 | 743 | 744 | class PokemonTypeSubResource(BaseResource): 745 | class Meta(BaseResource.Meta): 746 | name = 'Pokemon_Type' 747 | identifier = 'slot' 748 | attributes = ( 749 | 'slot', 750 | ) 751 | subresources = { 752 | 'type': NamedAPIResourceSubResource 753 | } 754 | 755 | def __repr__(self): 756 | return '<%s>' % self.Meta.name 757 | 758 | 759 | class PokemonHeldItemVersionSubResource(BaseResource): 760 | class Meta(BaseResource.Meta): 761 | name = 'Pokemon_Held_Item_Version' 762 | identifier = 'rarity' 763 | attributes = ( 764 | 'rarity', 765 | ) 766 | subresources = { 767 | 'version': NamedAPIResourceSubResource 768 | } 769 | 770 | def __repr__(self): 771 | return '<%s>' % self.Meta.name 772 | 773 | 774 | class PokemonHeldItemSubResource(BaseResource): 775 | class Meta(BaseResource.Meta): 776 | name = 'Pokemon_Held_Item' 777 | identifier = 'item' 778 | subresources = { 779 | 'item': NamedAPIResourceSubResource, 780 | 'version_details': PokemonHeldItemVersionSubResource 781 | } 782 | 783 | def __repr__(self): 784 | return '<%s>' % self.Meta.name 785 | 786 | 787 | class PokemonMoveVersionSubResource(BaseResource): 788 | class Meta(BaseResource.Meta): 789 | name = 'Pokemon_Move_Version' 790 | identifier = 'level_learned_at' 791 | attributes = ( 792 | 'level_learned_at', 793 | ) 794 | subresources = { 795 | 'move_learn_method': NamedAPIResourceSubResource, 796 | 'version_group': NamedAPIResourceSubResource 797 | } 798 | 799 | def __repr__(self): 800 | return '<%s>' % self.Meta.name 801 | 802 | 803 | class PokemonMoveSubResource(BaseResource): 804 | class Meta(BaseResource.Meta): 805 | name = 'Pokemon_Move' 806 | identifier = 'move' 807 | subresources = { 808 | 'move': NamedAPIResourceSubResource, 809 | 'version_group_details': PokemonMoveVersionSubResource 810 | } 811 | 812 | def __repr__(self): 813 | return '<%s>' % self.Meta.name 814 | 815 | 816 | class PokemonStatSubResource(BaseResource): 817 | class Meta(BaseResource.Meta): 818 | name = 'Pokemon_Stat' 819 | identifier = 'effort' 820 | attributes = ( 821 | 'effort', 822 | 'base_stat' 823 | ) 824 | subresources = { 825 | 'stat': NamedAPIResourceSubResource 826 | } 827 | 828 | def __repr__(self): 829 | return '<%s>' % self.Meta.name 830 | 831 | 832 | class PokemonSpritesSubResource(SubResource): 833 | class Meta(BaseResource.Meta): 834 | name = 'Pokemon_Sprites' 835 | identifier = 'front_default' 836 | attributes = ( 837 | 'front_default', 838 | 'front_shiny', 839 | 'front_female', 840 | 'front_shiny_female', 841 | 'back_default', 842 | 'back_shiny', 843 | 'back_female', 844 | 'back_shiny_female', 845 | ) 846 | 847 | def __repr__(self): 848 | return '<%s>' % self.Meta.name 849 | 850 | 851 | class LocationAreaEncounterSubResource(BaseResource): 852 | class Meta(BaseResource.Meta): 853 | name = 'Location_Area_Encounter' 854 | identifier = 'location_area' 855 | subresources = { 856 | 'location_area': NamedAPIResourceSubResource, 857 | 'version_details': VersionEncounterDetailSubResource 858 | } 859 | 860 | def __repr__(self): 861 | return '<%s>' % self.Meta.name 862 | 863 | 864 | class PokemonFormSpritesSubResource(SubResource): 865 | class Meta(BaseResource.Meta): 866 | name = 'Pokemon_Form_Sprites' 867 | identifier = 'front_default' 868 | attributes = ( 869 | 'front_default', 870 | 'front_shiny', 871 | 'back_default', 872 | 'back_shiny' 873 | ) 874 | 875 | def __repr__(self): 876 | return '<%s>' % self.Meta.name 877 | 878 | 879 | class AwesomeNameSubResource(BaseResource): 880 | class Meta(BaseResource.Meta): 881 | name = 'Awesome_Name' 882 | identifier = 'awesome_name' 883 | attributes = ( 884 | 'awesome_name', 885 | ) 886 | subresources = { 887 | 'language': NamedAPIResourceSubResource 888 | } 889 | 890 | def __repr__(self): 891 | return '<%s - %s>' % (self.Meta.name, self.awesome_name.capitalize()) 892 | 893 | 894 | class GenusSubResource(BaseResource): 895 | class Meta(BaseResource.Meta): 896 | name = 'Genus' 897 | identifier = 'genus' 898 | attributes = ( 899 | 'genus', 900 | ) 901 | subresources = { 902 | 'language': NamedAPIResourceSubResource 903 | } 904 | 905 | def __repr__(self): 906 | return '<%s - %s>' % (self.Meta.name, self.genus.capitalize()) 907 | 908 | 909 | class PokemonSpeciesDexEntrySubResource(BaseResource): 910 | class Meta(BaseResource.Meta): 911 | name = 'Pokemon_Species_Dex_Entry' 912 | identifier = 'entry_number' 913 | attributes = ( 914 | 'entry_number', 915 | ) 916 | subresources = { 917 | 'pokedex': NamedAPIResourceSubResource 918 | } 919 | 920 | def __repr__(self): 921 | return '<%s - %s>' % (self.Meta.name, self.entry_number) 922 | 923 | 924 | class PalParkEncounterAreaSubResource(BaseResource): 925 | class Meta(BaseResource.Meta): 926 | name = 'Pal_Park_Encounter_Area' 927 | identifier = 'base_score' 928 | attributes = ( 929 | 'base_score', 930 | 'rate' 931 | ) 932 | subresources = { 933 | 'area': NamedAPIResourceSubResource 934 | } 935 | 936 | def __repr__(self): 937 | return '<%s>' % self.Meta.name 938 | 939 | 940 | class PokemonSpeciesVarietySubResource(BaseResource): 941 | class Meta(BaseResource.Meta): 942 | name = 'Pokemon_Species_Variety' 943 | identifier = 'is_default' 944 | attributes = ( 945 | 'is_default', 946 | ) 947 | subresources = { 948 | 'pokemon': NamedAPIResourceSubResource 949 | } 950 | 951 | def __repr__(self): 952 | return '<%s>' % self.Meta.name 953 | 954 | 955 | class MoveStatAffectSubResource(BaseResource): 956 | class Meta(BaseResource.Meta): 957 | name = 'Move_Stat_Affect' 958 | identifier = 'change' 959 | attributes = ( 960 | 'change', 961 | ) 962 | subresources = { 963 | 'move': NamedAPIResourceSubResource 964 | } 965 | 966 | def __repr__(self): 967 | return '<%s>' % self.Meta.name 968 | 969 | 970 | class MoveStatAffectSetsSubResource(BaseResource): 971 | class Meta(BaseResource.Meta): 972 | name = 'Move_Stat_Affect_Sets' 973 | identifier = 'increase' 974 | subresources = { 975 | 'increase': MoveStatAffectSubResource, 976 | 'decrease': MoveStatAffectSubResource 977 | } 978 | 979 | def __repr__(self): 980 | return '<%s>' % self.Meta.name 981 | 982 | 983 | class NatureStatAffectSetsSubResource(BaseResource): 984 | class Meta(BaseResource.Meta): 985 | name = 'Nature_Stat_Affect_Sets' 986 | identifier = 'increase' 987 | subresources = { 988 | 'increase': NamedAPIResourceSubResource, 989 | 'decrease': NamedAPIResourceSubResource 990 | } 991 | 992 | def __repr__(self): 993 | return '<%s>' % self.Meta.name 994 | 995 | 996 | class TypePokemonSubResource(BaseResource): 997 | class Meta(BaseResource.Meta): 998 | name = 'Type_Pokemon' 999 | identifier = 'slot' 1000 | attributes = ( 1001 | 'slot', 1002 | ) 1003 | subresources = { 1004 | 'pokemon': NamedAPIResourceSubResource 1005 | } 1006 | 1007 | def __repr__(self): 1008 | return '<%s>' % self.Meta.name 1009 | 1010 | 1011 | class TypeRelationsSubResource(BaseResource): 1012 | class Meta(BaseResource.Meta): 1013 | name = 'Type_Relations' 1014 | identifier = 'no_damage_to' 1015 | subresources = { 1016 | 'no_damage_to': NamedAPIResourceSubResource, 1017 | 'half_damage_to': NamedAPIResourceSubResource, 1018 | 'double_damage_to': NamedAPIResourceSubResource, 1019 | 'no_damage_from': NamedAPIResourceSubResource, 1020 | 'half_damage_from': NamedAPIResourceSubResource, 1021 | 'double_damage_from': NamedAPIResourceSubResource 1022 | } 1023 | 1024 | def __repr__(self): 1025 | return '<%s>' % self.Meta.name 1026 | 1027 | 1028 | ########### 1029 | # Resources 1030 | ########### 1031 | 1032 | 1033 | class BerryResource(BaseResource): 1034 | 1035 | class Meta(BaseResource.Meta): 1036 | name = 'Berry' 1037 | resource_name = 'berry' 1038 | identifier = 'id' 1039 | methods = ( 1040 | 'get', 1041 | ) 1042 | attributes = ( 1043 | 'id', 1044 | 'name', 1045 | 'growth_time', 1046 | 'max_harvest', 1047 | 'natural_gift_power', 1048 | 'size', 1049 | 'smoothness', 1050 | 'soil_dryness' 1051 | ) 1052 | subresources = { 1053 | 'firmness': NamedAPIResourceSubResource, 1054 | 'flavors': BerryFlavorMapSubResource, 1055 | 'item': NamedAPIResourceSubResource, 1056 | 'natural_gift_type': NamedAPIResourceSubResource 1057 | } 1058 | 1059 | def __repr__(self): 1060 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1061 | 1062 | 1063 | class BerryFirmnessResource(BaseResource): 1064 | 1065 | class Meta(BaseResource.Meta): 1066 | name = 'Berry_Firmness' 1067 | resource_name = 'berry-firmness' 1068 | identifier = 'id' 1069 | methods = ( 1070 | 'get', 1071 | ) 1072 | attributes = ( 1073 | 'id', 1074 | 'name' 1075 | ) 1076 | subresources = { 1077 | 'berries': NamedAPIResourceSubResource, 1078 | 'names': NameSubResource 1079 | } 1080 | 1081 | def __repr__(self): 1082 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1083 | 1084 | 1085 | class BerryFlavorResource(BaseResource): 1086 | 1087 | class Meta(BaseResource.Meta): 1088 | name = 'Berry_Flavor' 1089 | resource_name = 'berry-flavor' 1090 | identifier = 'id' 1091 | methods = ( 1092 | 'get', 1093 | ) 1094 | attributes = ( 1095 | 'id', 1096 | 'name' 1097 | ) 1098 | subresources = { 1099 | 'berries': FlavorBerryMapSubResource, 1100 | 'contest_type': NamedAPIResourceSubResource, 1101 | 'names': NameSubResource 1102 | } 1103 | 1104 | def __repr__(self): 1105 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1106 | 1107 | 1108 | class ContestTypeResource(BaseResource): 1109 | 1110 | class Meta(BaseResource.Meta): 1111 | name = 'Contest_Type' 1112 | resource_name = 'contest-type' 1113 | identifier = 'id' 1114 | methods = ( 1115 | 'get', 1116 | ) 1117 | attributes = ( 1118 | 'id', 1119 | 'name' 1120 | ) 1121 | subresources = { 1122 | 'berry_flavor': NamedAPIResourceSubResource, 1123 | 'names': ContestNameSubResource 1124 | } 1125 | 1126 | def __repr__(self): 1127 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1128 | 1129 | 1130 | class ContestEffectResource(BaseResource): 1131 | 1132 | class Meta(BaseResource.Meta): 1133 | name = 'Contest_Effect' 1134 | resource_name = 'contest-effect' 1135 | identifier = 'id' 1136 | methods = ( 1137 | 'get', 1138 | ) 1139 | attributes = ( 1140 | 'id', 1141 | 'appeal', 1142 | 'jam' 1143 | ) 1144 | subresources = { 1145 | 'effect_entries': EffectSubResource, 1146 | 'flavor_text_entries': FlavorTextSubResource 1147 | } 1148 | 1149 | def __repr__(self): 1150 | return '<%s - %s>' % (self.Meta.name, self.id) 1151 | 1152 | 1153 | class SuperContestEffectResource(BaseResource): 1154 | 1155 | class Meta(BaseResource.Meta): 1156 | name = 'Super_Contest_Effect' 1157 | resource_name = 'super-contest-effect' 1158 | identifier = 'id' 1159 | methods = ( 1160 | 'get', 1161 | ) 1162 | attributes = ( 1163 | 'id', 1164 | 'appeal' 1165 | ) 1166 | subresources = { 1167 | 'flavor_text_entries': FlavorTextSubResource, 1168 | 'moves': NamedAPIResourceSubResource 1169 | } 1170 | 1171 | def __repr__(self): 1172 | return '<%s - %s>' % (self.Meta.name, self.id) 1173 | 1174 | 1175 | class EncounterMethodResource(BaseResource): 1176 | 1177 | class Meta(BaseResource.Meta): 1178 | name = 'Encounter_Method' 1179 | resource_name = 'encounter-method' 1180 | identifier = 'id' 1181 | methods = ( 1182 | 'get', 1183 | ) 1184 | attributes = ( 1185 | 'id', 1186 | 'name', 1187 | 'order' 1188 | ) 1189 | subresources = { 1190 | 'names': NameSubResource 1191 | } 1192 | 1193 | def __repr__(self): 1194 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1195 | 1196 | 1197 | class EncounterConditionResource(BaseResource): 1198 | 1199 | class Meta(BaseResource.Meta): 1200 | name = 'Encounter_Condition' 1201 | resource_name = 'encounter-condition' 1202 | identifier = 'id' 1203 | methods = ( 1204 | 'get', 1205 | ) 1206 | attributes = ( 1207 | 'id', 1208 | 'name' 1209 | ) 1210 | subresources = { 1211 | 'names': NameSubResource, 1212 | 'values': NamedAPIResourceSubResource 1213 | } 1214 | 1215 | def __repr__(self): 1216 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1217 | 1218 | 1219 | class EncounterConditionValueResource(BaseResource): 1220 | 1221 | class Meta(BaseResource.Meta): 1222 | name = 'Encounter_Condition_Value' 1223 | resource_name = 'encounter-condition-value' 1224 | identifier = 'id' 1225 | methods = ( 1226 | 'get', 1227 | ) 1228 | attributes = ( 1229 | 'id', 1230 | 'name' 1231 | ) 1232 | subresources = { 1233 | 'condition': NamedAPIResourceSubResource, 1234 | 'names': NameSubResource 1235 | } 1236 | 1237 | def __repr__(self): 1238 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1239 | 1240 | 1241 | class EvolutionChainResource(BaseResource): 1242 | 1243 | class Meta(BaseResource.Meta): 1244 | name = 'Evolution_Chain' 1245 | resource_name = 'evolution-chain' 1246 | identifier = 'id' 1247 | methods = ( 1248 | 'get', 1249 | ) 1250 | attributes = ( 1251 | 'id', 1252 | ) 1253 | subresources = { 1254 | 'baby_trigger_item': NamedAPIResourceSubResource, 1255 | 'chain': ChainLinkSubResource 1256 | } 1257 | 1258 | def __repr__(self): 1259 | return '<%s - %s>' % (self.Meta.name, self.id) 1260 | 1261 | 1262 | class EvolutionTriggerResource(BaseResource): 1263 | 1264 | class Meta(BaseResource.Meta): 1265 | name = 'Evolution_Trigger' 1266 | resource_name = 'evolution-trigger' 1267 | identifier = 'id' 1268 | methods = ( 1269 | 'get', 1270 | ) 1271 | attributes = ( 1272 | 'id', 1273 | 'name' 1274 | ) 1275 | subresources = { 1276 | 'names': NameSubResource, 1277 | 'pokemon_species': NamedAPIResourceSubResource 1278 | } 1279 | 1280 | def __repr__(self): 1281 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1282 | 1283 | 1284 | class GenerationResource(BaseResource): 1285 | 1286 | class Meta(BaseResource.Meta): 1287 | name = 'Generation' 1288 | resource_name = 'generation' 1289 | identifier = 'id' 1290 | methods = ( 1291 | 'get', 1292 | ) 1293 | attributes = ( 1294 | 'id', 1295 | 'name' 1296 | ) 1297 | subresources = { 1298 | 'abilities': NamedAPIResourceSubResource, 1299 | 'names': NameSubResource, 1300 | 'main_region': NamedAPIResourceSubResource, 1301 | 'moves': NamedAPIResourceSubResource, 1302 | 'pokemon_species': NamedAPIResourceSubResource, 1303 | 'types': NamedAPIResourceSubResource, 1304 | 'version_groups': NamedAPIResourceSubResource 1305 | } 1306 | 1307 | def __repr__(self): 1308 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1309 | 1310 | 1311 | class PokedexResource(BaseResource): 1312 | 1313 | class Meta(BaseResource.Meta): 1314 | name = 'Pokedex' 1315 | resource_name = 'pokedex' 1316 | identifier = 'id' 1317 | methods = ( 1318 | 'get', 1319 | ) 1320 | attributes = ( 1321 | 'id', 1322 | 'name', 1323 | 'is_main_series' 1324 | ) 1325 | subresources = { 1326 | 'descriptions': DescriptionSubResource, 1327 | 'names': NameSubResource, 1328 | 'pokemon_entries': PokemonEntrySubResource, 1329 | 'region': NamedAPIResourceSubResource, 1330 | 'version_groups': NamedAPIResourceSubResource 1331 | } 1332 | 1333 | def __repr__(self): 1334 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1335 | 1336 | 1337 | class VersionResource(BaseResource): 1338 | 1339 | class Meta(BaseResource.Meta): 1340 | name = 'Version' 1341 | resource_name = 'version' 1342 | identifier = 'id' 1343 | methods = ( 1344 | 'get', 1345 | ) 1346 | attributes = ( 1347 | 'id', 1348 | 'name' 1349 | ) 1350 | subresources = { 1351 | 'names': NameSubResource, 1352 | 'version_group': NamedAPIResourceSubResource 1353 | } 1354 | 1355 | def __repr__(self): 1356 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1357 | 1358 | 1359 | class VersionGroupResource(BaseResource): 1360 | 1361 | class Meta(BaseResource.Meta): 1362 | name = 'Version_Group' 1363 | resource_name = 'version-group' 1364 | identifier = 'id' 1365 | methods = ( 1366 | 'get', 1367 | ) 1368 | attributes = ( 1369 | 'id', 1370 | 'name', 1371 | 'order' 1372 | ) 1373 | subresources = { 1374 | 'generation': NamedAPIResourceSubResource, 1375 | 'move_learn_methods': NamedAPIResourceSubResource, 1376 | 'pokedexes': NamedAPIResourceSubResource, 1377 | 'regions': NamedAPIResourceSubResource, 1378 | 'versions': NamedAPIResourceSubResource 1379 | } 1380 | 1381 | def __repr__(self): 1382 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1383 | 1384 | 1385 | class ItemCategoryResource(BaseResource): 1386 | 1387 | class Meta(BaseResource.Meta): 1388 | name = 'Item_Category' 1389 | resource_name = 'item-category' 1390 | identifier = 'id' 1391 | methods = ( 1392 | 'get', 1393 | ) 1394 | attributes = ( 1395 | 'id', 1396 | 'name' 1397 | ) 1398 | subresources = { 1399 | 'items': NamedAPIResourceSubResource, 1400 | 'names': NameSubResource, 1401 | 'pocket': NamedAPIResourceSubResource 1402 | } 1403 | 1404 | def __repr__(self): 1405 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1406 | 1407 | 1408 | class ItemResource(BaseResource): 1409 | 1410 | class Meta(BaseResource.Meta): 1411 | name = 'Item' 1412 | resource_name = 'item' 1413 | identifier = 'id' 1414 | methods = ( 1415 | 'get', 1416 | ) 1417 | attributes = ( 1418 | 'id', 1419 | 'name', 1420 | 'cost', 1421 | 'fling_power' 1422 | ) 1423 | subresources = { 1424 | 'fling_effect': NamedAPIResourceSubResource, 1425 | 'attributes': NamedAPIResourceSubResource, 1426 | 'category': NamedAPIResourceSubResource, 1427 | 'effect_entries': VerboseEffectSubResource, 1428 | 'flavor_text_entries': VersionGroupFlavorTextSubResource, 1429 | 'game_indices': GenerationGameIndexSubResource, 1430 | 'names': NameSubResource, 1431 | 'sprites': ItemSpritesSubResource, 1432 | 'held_by_pokemon': ItemHolderPokemonSubResource, 1433 | 'baby_trigger_for': APIResourceSubResource, 1434 | 'machines': MachineVersionDetailSubResource 1435 | } 1436 | 1437 | def __repr__(self): 1438 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1439 | 1440 | 1441 | class ItemAttributeResource(BaseResource): 1442 | 1443 | class Meta(BaseResource.Meta): 1444 | name = 'Item_Attribute' 1445 | resource_name = 'item-attribute' 1446 | identifier = 'id' 1447 | methods = ( 1448 | 'get', 1449 | ) 1450 | attributes = ( 1451 | 'id', 1452 | 'name' 1453 | ) 1454 | subresources = { 1455 | 'items': NamedAPIResourceSubResource, 1456 | 'names': NameSubResource, 1457 | 'descriptions': DescriptionSubResource 1458 | } 1459 | 1460 | def __repr__(self): 1461 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1462 | 1463 | 1464 | class ItemFlingEffectResource(BaseResource): 1465 | 1466 | class Meta(BaseResource.Meta): 1467 | name = 'Item_Fling_Effect' 1468 | resource_name = 'item-fling-effect' 1469 | identifier = 'id' 1470 | methods = ( 1471 | 'get', 1472 | ) 1473 | attributes = ( 1474 | 'id', 1475 | 'name' 1476 | ) 1477 | subresources = { 1478 | 'effect_entries': EffectSubResource, 1479 | 'items': NamedAPIResourceSubResource 1480 | } 1481 | 1482 | def __repr__(self): 1483 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1484 | 1485 | 1486 | class ItemPocketResource(BaseResource): 1487 | 1488 | class Meta(BaseResource.Meta): 1489 | name = 'Item_Pocket' 1490 | resource_name = 'item-pocket' 1491 | identifier = 'id' 1492 | methods = ( 1493 | 'get', 1494 | ) 1495 | attributes = ( 1496 | 'id', 1497 | 'name' 1498 | ) 1499 | subresources = { 1500 | 'categories': NamedAPIResourceSubResource, 1501 | 'names': NameSubResource 1502 | } 1503 | 1504 | def __repr__(self): 1505 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1506 | 1507 | 1508 | class MachineResource(BaseResource): 1509 | 1510 | class Meta(BaseResource.Meta): 1511 | name = 'Machine' 1512 | resource_name = 'machine' 1513 | identifier = 'id' 1514 | methods = ( 1515 | 'get', 1516 | ) 1517 | attributes = ( 1518 | 'id', 1519 | ) 1520 | subresources = { 1521 | 'item': NamedAPIResourceSubResource, 1522 | 'move': NamedAPIResourceSubResource, 1523 | 'version_group': NamedAPIResourceSubResource 1524 | } 1525 | 1526 | def __repr__(self): 1527 | return '<%s - %s>' % (self.Meta.name, self.id) 1528 | 1529 | 1530 | class MoveResource(BaseResource): 1531 | 1532 | class Meta(BaseResource.Meta): 1533 | name = 'Move' 1534 | resource_name = 'move' 1535 | identifier = 'id' 1536 | methods = ( 1537 | 'get', 1538 | ) 1539 | attributes = ( 1540 | 'id', 1541 | 'name', 1542 | 'accuracy', 1543 | 'effect_chance', 1544 | 'pp', 1545 | 'priority', 1546 | 'power' 1547 | ) 1548 | subresources = { 1549 | 'contest_combos': ContestComboSetsSubResource, 1550 | 'contest_type': NamedAPIResourceSubResource, 1551 | 'contest_effect': APIResourceSubResource, 1552 | 'damage_class': NamedAPIResourceSubResource, 1553 | 'effect_entries': VerboseEffectSubResource, 1554 | 'effect_changes': AbilityEffectChangeSubResource, 1555 | 'learned_by_pokemon': NamedAPIResourceSubResource, 1556 | 'flavor_text_entries': MoveFlavorTextSubResource, 1557 | 'generation': NamedAPIResourceSubResource, 1558 | 'machines': MachineVersionDetailSubResource, 1559 | 'meta': MoveMetaDataSubResource, 1560 | 'names': NameSubResource, 1561 | 'past_values': PastMoveStatValuesSubResource, 1562 | 'stat_changes': MoveStatChangeSubResource, 1563 | 'super_contest_effect': APIResourceSubResource, 1564 | 'target': NamedAPIResourceSubResource, 1565 | 'type': NamedAPIResourceSubResource 1566 | } 1567 | 1568 | def __repr__(self): 1569 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1570 | 1571 | 1572 | class MoveAilmentResource(BaseResource): 1573 | 1574 | class Meta(BaseResource.Meta): 1575 | name = 'Move_Ailment' 1576 | resource_name = 'move-ailment' 1577 | identifier = 'id' 1578 | methods = ( 1579 | 'get', 1580 | ) 1581 | attributes = ( 1582 | 'id', 1583 | 'name' 1584 | ) 1585 | subresources = { 1586 | 'moves': NamedAPIResourceSubResource, 1587 | 'names': NameSubResource 1588 | } 1589 | 1590 | def __repr__(self): 1591 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1592 | 1593 | 1594 | class MoveBattleStyleResource(BaseResource): 1595 | 1596 | class Meta(BaseResource.Meta): 1597 | name = 'Move_Battle_Style' 1598 | resource_name = 'move-battle-style' 1599 | identifier = 'id' 1600 | methods = ( 1601 | 'get', 1602 | ) 1603 | attributes = ( 1604 | 'id', 1605 | 'name' 1606 | ) 1607 | subresources = { 1608 | 'names': NameSubResource 1609 | } 1610 | 1611 | def __repr__(self): 1612 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1613 | 1614 | 1615 | class MoveCategoryResource(BaseResource): 1616 | 1617 | class Meta(BaseResource.Meta): 1618 | name = 'Move_Category' 1619 | resource_name = 'move-category' 1620 | identifier = 'id' 1621 | methods = ( 1622 | 'get', 1623 | ) 1624 | attributes = ( 1625 | 'id', 1626 | 'name' 1627 | ) 1628 | subresources = { 1629 | 'moves': NamedAPIResourceSubResource, 1630 | 'descriptions': DescriptionSubResource 1631 | } 1632 | 1633 | def __repr__(self): 1634 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1635 | 1636 | 1637 | class MoveDamageClassResource(BaseResource): 1638 | 1639 | class Meta(BaseResource.Meta): 1640 | name = 'Move_Damage_Class' 1641 | resource_name = 'move-damage-class' 1642 | identifier = 'id' 1643 | methods = ( 1644 | 'get', 1645 | ) 1646 | attributes = ( 1647 | 'id', 1648 | 'name' 1649 | ) 1650 | subresources = { 1651 | 'descriptions': DescriptionSubResource, 1652 | 'moves': NamedAPIResourceSubResource, 1653 | 'names': NameSubResource 1654 | } 1655 | 1656 | def __repr__(self): 1657 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1658 | 1659 | 1660 | class MoveLearnMethodResource(BaseResource): 1661 | 1662 | class Meta(BaseResource.Meta): 1663 | name = 'Move_Learn_Method' 1664 | resource_name = 'move-learn-method' 1665 | identifier = 'id' 1666 | methods = ( 1667 | 'get', 1668 | ) 1669 | attributes = ( 1670 | 'id', 1671 | 'name' 1672 | ) 1673 | subresources = { 1674 | 'descriptions': DescriptionSubResource, 1675 | 'names': NameSubResource, 1676 | 'version_groups': NamedAPIResourceSubResource 1677 | } 1678 | 1679 | def __repr__(self): 1680 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1681 | 1682 | 1683 | class MoveTargetResource(BaseResource): 1684 | 1685 | class Meta(BaseResource.Meta): 1686 | name = 'Move_Target' 1687 | resource_name = 'move-target' 1688 | identifier = 'id' 1689 | methods = ( 1690 | 'get', 1691 | ) 1692 | attributes = ( 1693 | 'id', 1694 | 'name' 1695 | ) 1696 | subresources = { 1697 | 'descriptions': DescriptionSubResource, 1698 | 'moves': NamedAPIResourceSubResource, 1699 | 'names': NameSubResource 1700 | } 1701 | 1702 | def __repr__(self): 1703 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1704 | 1705 | 1706 | class LocationResource(BaseResource): 1707 | 1708 | class Meta(BaseResource.Meta): 1709 | name = 'Location' 1710 | resource_name = 'location' 1711 | identifier = 'id' 1712 | methods = ( 1713 | 'get', 1714 | ) 1715 | attributes = ( 1716 | 'id', 1717 | 'name' 1718 | ) 1719 | subresources = { 1720 | 'region': NamedAPIResourceSubResource, 1721 | 'names': NameSubResource, 1722 | 'game_indices': GenerationGameIndexSubResource, 1723 | 'areas': NamedAPIResourceSubResource 1724 | } 1725 | 1726 | def __repr__(self): 1727 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1728 | 1729 | 1730 | class LocationAreaResource(BaseResource): 1731 | 1732 | class Meta(BaseResource.Meta): 1733 | name = 'Location_Area' 1734 | resource_name = 'location-area' 1735 | identifier = 'id' 1736 | methods = ( 1737 | 'get', 1738 | ) 1739 | attributes = ( 1740 | 'id', 1741 | 'name', 1742 | 'game_index' 1743 | ) 1744 | subresources = { 1745 | 'encounter_method_rates': EncounterMethodRateSubResource, 1746 | 'location': NamedAPIResourceSubResource, 1747 | 'names': NameSubResource, 1748 | 'pokemon_encounters': PokemonEncounterSubResource 1749 | } 1750 | 1751 | def __repr__(self): 1752 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1753 | 1754 | 1755 | class PalParkAreaResource(BaseResource): 1756 | 1757 | class Meta(BaseResource.Meta): 1758 | name = 'Pal_Park_Area' 1759 | resource_name = 'pal-park-area' 1760 | identifier = 'id' 1761 | methods = ( 1762 | 'get', 1763 | ) 1764 | attributes = ( 1765 | 'id', 1766 | 'name' 1767 | ) 1768 | subresources = { 1769 | 'names': NameSubResource, 1770 | 'pokemon_encounters': PalParkEncounterSpeciesSubResource 1771 | } 1772 | 1773 | def __repr__(self): 1774 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1775 | 1776 | 1777 | class RegionResource(BaseResource): 1778 | 1779 | class Meta(BaseResource.Meta): 1780 | name = 'Region' 1781 | resource_name = 'region' 1782 | identifier = 'id' 1783 | methods = ( 1784 | 'get', 1785 | ) 1786 | attributes = ( 1787 | 'id', 1788 | 'name' 1789 | ) 1790 | subresources = { 1791 | 'locations': NamedAPIResourceSubResource, 1792 | 'main_generation': NamedAPIResourceSubResource, 1793 | 'names': NameSubResource, 1794 | 'pokedexes': NamedAPIResourceSubResource, 1795 | 'version_groups': NamedAPIResourceSubResource 1796 | } 1797 | 1798 | def __repr__(self): 1799 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1800 | 1801 | 1802 | class AbilityResource(BaseResource): 1803 | 1804 | class Meta(BaseResource.Meta): 1805 | name = 'Ability' 1806 | resource_name = 'ability' 1807 | identifier = 'id' 1808 | methods = ( 1809 | 'get', 1810 | ) 1811 | attributes = ( 1812 | 'id', 1813 | 'name', 1814 | 'is_main_series' 1815 | ) 1816 | subresources = { 1817 | 'generation': NamedAPIResourceSubResource, 1818 | 'names': NameSubResource, 1819 | 'effect_entries': VerboseEffectSubResource, 1820 | 'effect_changes': AbilityEffectChangeSubResource, 1821 | 'flavor_text_entries': AbilityFlavorTextSubResource, 1822 | 'pokemon': AbilityPokemonSubResource 1823 | } 1824 | 1825 | def __repr__(self): 1826 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1827 | 1828 | 1829 | class CharacteristicResource(BaseResource): 1830 | 1831 | class Meta(BaseResource.Meta): 1832 | name = 'Characteristic' 1833 | resource_name = 'characteristic' 1834 | identifier = 'id' 1835 | methods = ( 1836 | 'get', 1837 | ) 1838 | attributes = ( 1839 | 'id', 1840 | 'gene_modulo', 1841 | 'possible_values' 1842 | ) 1843 | subresources = { 1844 | 'descriptions': DescriptionSubResource 1845 | } 1846 | 1847 | def __repr__(self): 1848 | return '<%s - %s>' % (self.Meta.name, self.id) 1849 | 1850 | 1851 | class EggGroupResource(BaseResource): 1852 | 1853 | class Meta(BaseResource.Meta): 1854 | name = 'Egg_Group' 1855 | resource_name = 'egg-group' 1856 | identifier = 'id' 1857 | methods = ( 1858 | 'get', 1859 | ) 1860 | attributes = ( 1861 | 'id', 1862 | 'name' 1863 | ) 1864 | subresources = { 1865 | 'names': NameSubResource, 1866 | 'pokemon_species': NamedAPIResourceSubResource 1867 | } 1868 | 1869 | def __repr__(self): 1870 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1871 | 1872 | 1873 | class GenderResource(BaseResource): 1874 | 1875 | class Meta(BaseResource.Meta): 1876 | name = 'Gender' 1877 | resource_name = 'gender' 1878 | identifier = 'id' 1879 | methods = ( 1880 | 'get', 1881 | ) 1882 | attributes = ( 1883 | 'id', 1884 | 'name' 1885 | ) 1886 | subresources = { 1887 | 'pokemon_species_details': PokemonSpeciesGenderSubResource, 1888 | 'required_for_evolution': NamedAPIResourceSubResource 1889 | } 1890 | 1891 | def __repr__(self): 1892 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1893 | 1894 | 1895 | class GrowthRateResource(BaseResource): 1896 | 1897 | class Meta(BaseResource.Meta): 1898 | name = 'Growth_Rate' 1899 | resource_name = 'growth-rate' 1900 | identifier = 'id' 1901 | methods = ( 1902 | 'get', 1903 | ) 1904 | attributes = ( 1905 | 'id', 1906 | 'name', 1907 | 'formula' 1908 | ) 1909 | subresources = { 1910 | 'descriptions': DescriptionSubResource, 1911 | 'levels': GrowthRateExperienceLevelSubResource, 1912 | 'pokemon_species': NamedAPIResourceSubResource 1913 | } 1914 | 1915 | def __repr__(self): 1916 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1917 | 1918 | 1919 | class NatureResource(BaseResource): 1920 | 1921 | class Meta(BaseResource.Meta): 1922 | name = 'Nature' 1923 | resource_name = 'nature' 1924 | identifier = 'id' 1925 | methods = ( 1926 | 'get', 1927 | ) 1928 | attributes = ( 1929 | 'id', 1930 | 'name', 1931 | ) 1932 | subresources = { 1933 | 'decreased_stat': NamedAPIResourceSubResource, 1934 | 'increased_stat': NamedAPIResourceSubResource, 1935 | 'hates_flavor': NamedAPIResourceSubResource, 1936 | 'likes_flavor': NamedAPIResourceSubResource, 1937 | 'pokeathlon_stat_changes': NatureStatChangeSubResource, 1938 | 'move_battle_style_preferences': MoveBattleStylePreferenceSubResource, 1939 | 'names': NameSubResource 1940 | } 1941 | 1942 | def __repr__(self): 1943 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1944 | 1945 | 1946 | class PokeathlonStatResource(BaseResource): 1947 | 1948 | class Meta(BaseResource.Meta): 1949 | name = 'Pokeathlon_Stat' 1950 | resource_name = 'pokeathlon-stat' 1951 | identifier = 'id' 1952 | methods = ( 1953 | 'get', 1954 | ) 1955 | attributes = ( 1956 | 'id', 1957 | 'name', 1958 | ) 1959 | subresources = { 1960 | 'names': NameSubResource, 1961 | 'affecting_natures': NaturePokeathlonStatAffectSetsSubResource 1962 | } 1963 | 1964 | def __repr__(self): 1965 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 1966 | 1967 | 1968 | class PokemonResource(BaseResource): 1969 | 1970 | class Meta(BaseResource.Meta): 1971 | name = 'Pokemon' 1972 | resource_name = 'pokemon' 1973 | identifier = 'id' 1974 | methods = ( 1975 | 'get', 1976 | ) 1977 | attributes = ( 1978 | 'id', 1979 | 'name', 1980 | 'base_experience', 1981 | 'height', 1982 | 'is_default', 1983 | 'order', 1984 | 'weight', 1985 | 'location_area_encounters' 1986 | ) 1987 | subresources = { 1988 | 'abilities': PokemonAbilitySubResource, 1989 | 'forms': NamedAPIResourceSubResource, 1990 | 'game_indices': VersionGameIndexSubResource, 1991 | 'held_items': PokemonHeldItemSubResource, 1992 | 'moves': PokemonMoveSubResource, 1993 | 'sprites': PokemonSpritesSubResource, 1994 | 'species': NamedAPIResourceSubResource, 1995 | 'stats': PokemonStatSubResource, 1996 | 'types': PokemonTypeSubResource 1997 | } 1998 | 1999 | def __repr__(self): 2000 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2001 | 2002 | 2003 | class PokemonColorResource(BaseResource): 2004 | 2005 | class Meta(BaseResource.Meta): 2006 | name = 'Pokemon_Color' 2007 | resource_name = 'pokemon-color' 2008 | identifier = 'id' 2009 | methods = ( 2010 | 'get', 2011 | ) 2012 | attributes = ( 2013 | 'id', 2014 | 'name', 2015 | ) 2016 | subresources = { 2017 | 'names': NameSubResource, 2018 | 'pokemon_species': NamedAPIResourceSubResource 2019 | } 2020 | 2021 | def __repr__(self): 2022 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2023 | 2024 | 2025 | class PokemonFormResource(BaseResource): 2026 | 2027 | class Meta(BaseResource.Meta): 2028 | name = 'Pokemon_Form' 2029 | resource_name = 'pokemon-form' 2030 | identifier = 'id' 2031 | methods = ( 2032 | 'get', 2033 | ) 2034 | attributes = ( 2035 | 'id', 2036 | 'name', 2037 | 'order', 2038 | 'form_order', 2039 | 'is_default', 2040 | 'is_battle_only', 2041 | 'is_mega', 2042 | 'form_name', 2043 | ) 2044 | subresources = { 2045 | 'pokemon': NamedAPIResourceSubResource, 2046 | 'sprites': PokemonFormSpritesSubResource, 2047 | 'version_group': NamedAPIResourceSubResource, 2048 | 'names': NameSubResource, 2049 | 'form_names': NameSubResource 2050 | } 2051 | 2052 | def __repr__(self): 2053 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2054 | 2055 | 2056 | class PokemonHabitatResource(BaseResource): 2057 | 2058 | class Meta(BaseResource.Meta): 2059 | name = 'Pokemon_Habitat' 2060 | resource_name = 'pokemon-habitat' 2061 | identifier = 'id' 2062 | methods = ( 2063 | 'get', 2064 | ) 2065 | attributes = ( 2066 | 'id', 2067 | 'name', 2068 | ) 2069 | subresources = { 2070 | 'names': NameSubResource, 2071 | 'pokemon_species': NamedAPIResourceSubResource 2072 | } 2073 | 2074 | def __repr__(self): 2075 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2076 | 2077 | 2078 | class PokemonShapeResource(BaseResource): 2079 | 2080 | class Meta(BaseResource.Meta): 2081 | name = 'Pokemon_Shape' 2082 | resource_name = 'pokemon-shape' 2083 | identifier = 'id' 2084 | methods = ( 2085 | 'get', 2086 | ) 2087 | attributes = ( 2088 | 'id', 2089 | 'name', 2090 | ) 2091 | subresources = { 2092 | 'awesome_names': AwesomeNameSubResource, 2093 | 'names': NameSubResource, 2094 | 'pokemon_species': NamedAPIResourceSubResource 2095 | } 2096 | 2097 | def __repr__(self): 2098 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2099 | 2100 | 2101 | class PokemonSpeciesResource(BaseResource): 2102 | 2103 | class Meta(BaseResource.Meta): 2104 | name = 'Pokemon_Species' 2105 | resource_name = 'pokemon-species' 2106 | identifier = 'id' 2107 | methods = ( 2108 | 'get', 2109 | ) 2110 | attributes = ( 2111 | 'id', 2112 | 'name', 2113 | 'order', 2114 | 'gender_rate', 2115 | 'capture_rate', 2116 | 'base_happiness', 2117 | 'is_baby', 2118 | 'is_legendary', 2119 | 'is_mythical', 2120 | 'hatch_counter', 2121 | 'has_gender_differences', 2122 | 'forms_switchable', 2123 | ) 2124 | subresources = { 2125 | 'growth_rate': NamedAPIResourceSubResource, 2126 | 'pokedex_numbers': PokemonSpeciesDexEntrySubResource, 2127 | 'egg_groups': NamedAPIResourceSubResource, 2128 | 'color': NamedAPIResourceSubResource, 2129 | 'shape': NamedAPIResourceSubResource, 2130 | 'evolves_from_species': NamedAPIResourceSubResource, 2131 | 'evolution_chain': APIResourceSubResource, 2132 | 'habitat': NamedAPIResourceSubResource, 2133 | 'generation': NamedAPIResourceSubResource, 2134 | 'names': NameSubResource, 2135 | 'pal_park_encounters': PalParkEncounterAreaSubResource, 2136 | 'flavor_text_entries': FlavorTextSubResource, 2137 | 'form_descriptions': DescriptionSubResource, 2138 | 'genera': GenusSubResource, 2139 | 'varieties': PokemonSpeciesVarietySubResource 2140 | } 2141 | 2142 | def __repr__(self): 2143 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2144 | 2145 | 2146 | class StatResource(BaseResource): 2147 | 2148 | class Meta(BaseResource.Meta): 2149 | name = 'Stat' 2150 | resource_name = 'stat' 2151 | identifier = 'id' 2152 | methods = ( 2153 | 'get', 2154 | ) 2155 | attributes = ( 2156 | 'id', 2157 | 'name', 2158 | 'game_index', 2159 | 'is_battle_only', 2160 | ) 2161 | subresources = { 2162 | 'affecting_moves': MoveStatAffectSetsSubResource, 2163 | 'affecting_natures': NatureStatAffectSetsSubResource, 2164 | 'characteristics': APIResourceSubResource, 2165 | 'move_damage_class': NamedAPIResourceSubResource, 2166 | 'names': NameSubResource 2167 | } 2168 | 2169 | def __repr__(self): 2170 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2171 | 2172 | 2173 | class TypeResource(BaseResource): 2174 | 2175 | class Meta(BaseResource.Meta): 2176 | name = 'Type' 2177 | resource_name = 'type' 2178 | identifier = 'id' 2179 | methods = ( 2180 | 'get', 2181 | ) 2182 | attributes = ( 2183 | 'id', 2184 | 'name', 2185 | ) 2186 | subresources = { 2187 | 'damage_relations': TypeRelationsSubResource, 2188 | 'game_indices': GenerationGameIndexSubResource, 2189 | 'generation': NamedAPIResourceSubResource, 2190 | 'move_damage_class': NamedAPIResourceSubResource, 2191 | 'names': NameSubResource, 2192 | 'pokemon': TypePokemonSubResource, 2193 | 'moves': NamedAPIResourceSubResource 2194 | } 2195 | 2196 | def __repr__(self): 2197 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2198 | 2199 | 2200 | class LanguageResource(BaseResource): 2201 | 2202 | class Meta(BaseResource.Meta): 2203 | name = 'Language' 2204 | resource_name = 'language' 2205 | identifier = 'id' 2206 | methods = ( 2207 | 'get', 2208 | ) 2209 | attributes = ( 2210 | 'id', 2211 | 'name', 2212 | 'official', 2213 | 'iso639', 2214 | 'iso3166', 2215 | ) 2216 | subresources = { 2217 | 'names': NameSubResource 2218 | } 2219 | 2220 | def __repr__(self): 2221 | return '<%s - %s>' % (self.Meta.name, self.name.capitalize()) 2222 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements-dev27.txt 2 | pylint==2.1.* # style 3 | twine==1.12.* # pypi tools 4 | pyOpenSSL==19.0.* # twine dependency 5 | readme_renderer[md]==24.0.* # command 'twine check' 6 | wheel==0.32.* # command 'setup.py sdist bdist_wheel' 7 | -------------------------------------------------------------------------------- /requirements-dev27.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.0.* # docs 2 | mkdocs-material==3.2.* # docs 3 | coverage==4.5.* # coverage 4 | requests-mock==1.5.* # tests 5 | tox==3.5.* # tests 6 | PyYAML==5.2 # mkdocs dependency (last version to support Python 3.4) 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.* # dependency of FileCache 2 | beckett==0.8.* # main functionality 3 | requests==2.21.* # used by beckett (bumped to avoid CVE-2018-18074) 4 | urllib3~=1.24.3 # used by requests (bumped to avoid CVE-2019-11236) 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | import pokepy 5 | 6 | 7 | try: 8 | from setuptools import setup 9 | except ImportError: 10 | from distutils.core import setup 11 | 12 | with open('README.md', encoding='utf-8') as readme_md,\ 13 | open('docs/history.md', encoding='utf-8') as history_md,\ 14 | open('requirements.txt', encoding='utf-8') as requirements_txt: 15 | readme = readme_md.read() 16 | history = history_md.read() 17 | requirements = [req[:req.find('#')].rstrip() for req in requirements_txt.readlines()] 18 | 19 | setup( 20 | name='pokepy', 21 | version=pokepy.__version__, 22 | description='A Python wrapper for PokéAPI (https://pokeapi.co)', 23 | long_description=readme + '\n\n' + history, 24 | long_description_content_type='text/markdown', 25 | license=pokepy.__license__, 26 | author=pokepy.__author__, 27 | author_email=pokepy.__email__, 28 | url='https://github.com/PokeAPI/pokepy', 29 | project_urls={'Documentation': 'https://pokeapi.github.io/pokepy/'}, 30 | packages=['pokepy', 'pokepy.fcache'], 31 | package_dir={'pokepy': 'pokepy'}, 32 | include_package_data=True, 33 | install_requires=requirements, 34 | zip_safe=False, 35 | keywords='pokepy PokéAPI', 36 | classifiers=[ 37 | 'Development Status :: 4 - Beta', 38 | 'Intended Audience :: Developers', 39 | 'License :: OSI Approved :: BSD License', 40 | 'Natural Language :: English', 41 | 'Programming Language :: Python :: 2', 42 | 'Programming Language :: Python :: 2.7', 43 | 'Programming Language :: Python :: 3', 44 | 'Programming Language :: Python :: 3.4', 45 | 'Programming Language :: Python :: 3.5', 46 | 'Programming Language :: Python :: 3.6', 47 | 'Programming Language :: Python :: 3.7', 48 | 'Programming Language :: Python :: 3.8' 49 | ], 50 | test_suite='tests', 51 | tests_require=['requests-mock==1.5.*'], 52 | ) 53 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py34,py35,py36,py37,py38 3 | 4 | [testenv] 5 | setenv = 6 | PYTHONPATH = {toxinidir}:{toxinidir}/pokepy 7 | commands = python setup.py test 8 | deps = 9 | -r{toxinidir}/requirements.txt 10 | --------------------------------------------------------------------------------