diff --git a/.flake8 b/.flake8 index 54dde48..ae4540b 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] -max-line-length = 95 +max-line-length = 100 max-complexity = 18 select = B,C,D,E,F,W,T4,B9 ignore = W503 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index e8b5601..2497e81 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] os: - ubuntu-latest - macos-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e044f66..baf8d56 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,16 +4,16 @@ repos: hooks: - id: seed-isort-config - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/ambv/black - rev: 23.9.1 + rev: 24.1.1 hooks: - id: black language_version: python3.11 - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: @@ -22,4 +22,4 @@ repos: rev: v3.15.0 hooks: - id: pyupgrade - args: ["--py36-plus", "--py37-plus", "--py38-plus"] + args: ["--py36-plus", "--py37-plus", "--py38-plus", "--py39-plus"] diff --git a/.readthedocs.yml b/.readthedocs.yml index 8ae03ae..2c5c771 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,7 +11,7 @@ sphinx: # Optionally set the version of Python and requirements required to build your docs python: - version: "3.9" + version: "3.10" install: - method: pip path: . diff --git a/docs/source/conf.py b/docs/source/conf.py index 2200190..9019357 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,4 +1,5 @@ """Documentation configuration.""" + # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full diff --git a/docs/source/mokkari.rst b/docs/source/mokkari.rst index b041c62..3c13fe3 100644 --- a/docs/source/mokkari.rst +++ b/docs/source/mokkari.rst @@ -1,32 +1,16 @@ mokkari package =============== -Submodules ----------- +Subpackages +----------- -mokkari.arc module ------------------- - -.. automodule:: mokkari.arc - :members: - :undoc-members: - :show-inheritance: +.. toctree:: + :maxdepth: 4 -mokkari.character module ------------------------- + mokkari.schemas -.. automodule:: mokkari.character - :members: - :undoc-members: - :show-inheritance: - -mokkari.creator module ----------------------- - -.. automodule:: mokkari.creator - :members: - :undoc-members: - :show-inheritance: +Submodules +---------- mokkari.exceptions module ------------------------- @@ -36,54 +20,6 @@ mokkari.exceptions module :undoc-members: :show-inheritance: -mokkari.genre module --------------------- - -.. automodule:: mokkari.genre - :members: - :undoc-members: - :show-inheritance: - -mokkari.issue module --------------------- - -.. automodule:: mokkari.issue - :members: - :undoc-members: - :show-inheritance: - -mokkari.publisher module ------------------------- - -.. automodule:: mokkari.publisher - :members: - :undoc-members: - :show-inheritance: - -mokkari.rating module ---------------------- - -.. automodule:: mokkari.rating - :members: - :undoc-members: - :show-inheritance: - -mokkari.reprint module ----------------------- - -.. automodule:: mokkari.reprint - :members: - :undoc-members: - :show-inheritance: - -mokkari.series module ---------------------- - -.. automodule:: mokkari.series - :members: - :undoc-members: - :show-inheritance: - mokkari.session module ---------------------- @@ -100,22 +36,6 @@ mokkari.sqlite\_cache module :undoc-members: :show-inheritance: -mokkari.team module -------------------- - -.. automodule:: mokkari.team - :members: - :undoc-members: - :show-inheritance: - -mokkari.variant module ----------------------- - -.. automodule:: mokkari.variant - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/docs/source/mokkari.schemas.rst b/docs/source/mokkari.schemas.rst new file mode 100644 index 0000000..15d0f30 --- /dev/null +++ b/docs/source/mokkari.schemas.rst @@ -0,0 +1,93 @@ +mokkari.schemas package +======================= + +Submodules +---------- + +mokkari.schemas.arc module +-------------------------- + +.. automodule:: mokkari.schemas.arc + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.character module +-------------------------------- + +.. automodule:: mokkari.schemas.character + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.creator module +------------------------------ + +.. automodule:: mokkari.schemas.creator + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.generic module +------------------------------ + +.. automodule:: mokkari.schemas.generic + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.issue module +---------------------------- + +.. automodule:: mokkari.schemas.issue + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.publisher module +-------------------------------- + +.. automodule:: mokkari.schemas.publisher + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.reprint module +------------------------------ + +.. automodule:: mokkari.schemas.reprint + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.series module +----------------------------- + +.. automodule:: mokkari.schemas.series + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.team module +--------------------------- + +.. automodule:: mokkari.schemas.team + :members: + :undoc-members: + :show-inheritance: + +mokkari.schemas.variant module +------------------------------ + +.. automodule:: mokkari.schemas.variant + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: mokkari.schemas + :members: + :undoc-members: + :show-inheritance: diff --git a/mokkari/arc.py b/mokkari/arc.py deleted file mode 100644 index f8842e8..0000000 --- a/mokkari/arc.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -Arc module. - -This module provides the following classes: - -- Arc -- ArcSchema -- ArcsList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import exceptions - - -class Arc: - """ - The Arc object contains information for story arcs. - - Args: - **kwargs (Any): The keyword arguments is used for setting arc data from Metron. - - Attributes: - id (int): The Metron identification number for the story arc. - name (str): The name of the story arc. - desc (str): The description of the story arc. - image (url): The url for an image associated with the story arc. - cv_id (int): Comic Vine ID for the story arc. - resource_url (url): The url for the resource. - modified (datetime): The date/time the story arc was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Arc.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class ArcSchema(Schema): - """ - Schema for the Arc API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - desc = fields.Str() - image = fields.Url(allow_none=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the arc object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - An :obj:`Arc` object - """ - return Arc(**data) - - -class ArcsList: - """ - The :obj:`ArcsList` object contains a list of story arcs. - - Attributes: - id (int): The Metron identification number for the story arc. - name (str): The name of the story arc. - modified (datetime): The date/time the story arc was last changed. - - Returns: - A list of story arcs. - """ - - def __init__(self, response): - """Initialize a new ArcsList.""" - self.arcs = [] - - schema = ArcSchema() - for arc_dict in response["results"]: - try: - result = schema.load(arc_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.arcs.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.arcs) - - def __len__(self): - """Return the length of the object.""" - return len(self.arcs) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.arcs[index] diff --git a/mokkari/character.py b/mokkari/character.py deleted file mode 100644 index a852691..0000000 --- a/mokkari/character.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Character module. - -This module provides the following classes: - -- Character -- CharacterSchema -- CharactersList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import creator, exceptions, team - - -class Character: - """ - The Character object contains information for characters. - - Args: - **kwargs (Any): The keyword arguments is used for setting character data from Metron. - - Attributes: - id (int): The Metron identification number for the character. - name (str): The name of the character. - alias (list[str]): List of aliases the character may have. - desc (str): The description of the character. - image (url): The url for an image associated with the character. - creators (list[Creator]): A list of creators for the character. - teams (list[Team]): A list of teams the character is a member of. - cv_id (int): Comic Vine ID for the Character. - resource_url (url): The url for the resource. - modified (datetime): The date/time the character was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Character.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class CharacterSchema(Schema): - """ - Schema for the Character API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 2.0.2 - - Removed ``wikipedia`` field - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - alias = fields.List(fields.Str) - desc = fields.Str() - image = fields.Url(allow_none=True) - creators = fields.Nested(creator.CreatorSchema, many=True) - teams = fields.Nested(team.TeamSchema, many=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the character object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Character` object - """ - return Character(**data) - - -class CharactersList: - """ - The :obj:`CharactersList` object contains a list of characters. - - Attributes: - id (int): The Metron identification number for the character. - name (str): The name of the character. - modified (datetime): The date/time the character was last changed. - - Returns: - A list of characters. - """ - - def __init__(self, response): - """Initialize a new CharactersList.""" - self.characters = [] - - schema = CharacterSchema() - for character_dict in response["results"]: - try: - result = schema.load(character_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.characters.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.characters) - - def __len__(self): - """Return the length of the object.""" - return len(self.characters) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.characters[index] diff --git a/mokkari/creator.py b/mokkari/creator.py deleted file mode 100644 index 5450e03..0000000 --- a/mokkari/creator.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Creator module. - -This module provides the following classes: - -- Creator -- CreatorSchema -- CreatorsList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import exceptions - - -class Creator: - """ - The Creator object contains information for creators. - - Args: - **kwargs (Any): The keyword arguments is used for setting creator data from Metron. - - Attributes: - id (int): The Metron identification number for the creator. - name (str): The name of the creator. - birth (date): The date of birth for the creator. - death (date): The date of death for the creator. - desc (str): The description of the creator. - image (url): The url for an image associated with the creator. - cv_id (int): Comic Vine ID for the Creator. - resource_url (url): The url for the resource. - modified (datetime): The date/time the creator was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Creator.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class CreatorSchema(Schema): - """ - Schema for the Creator API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 2.0.2 - - - Removed ``wikipedia`` field - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - birth = fields.Date(allow_none=True) - death = fields.Date(allow_none=True) - desc = fields.Str() - image = fields.Url(allow_none=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - dateformat = "%Y-%m-%d" - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Creator object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Creator` object. - """ - return Creator(**data) - - -class CreatorsList: - """ - The :obj:`CreatorsList` object contains a list of creators. - - Attributes: - id (int): The Metron identification number for the creator. - name (str): The name of the creator. - modified (datetime): The date/time the creator was last changed. - - Returns: - A list of creators. - """ - - def __init__(self, response) -> None: - """Initialize a new CreatorsList.""" - self.creators = [] - - schema = CreatorSchema() - for creator_dict in response["results"]: - try: - result = schema.load(creator_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.creators.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.creators) - - def __len__(self): - """Return the length of the object.""" - return len(self.creators) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.creators[index] diff --git a/mokkari/genre.py b/mokkari/genre.py deleted file mode 100644 index b3c8ea7..0000000 --- a/mokkari/genre.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Genre Module. - -This module provides the following classes: - -- Genre -- GenreSchema -""" -from marshmallow import EXCLUDE, Schema, fields, post_load - - -class Genre: - """ - The Genre object contains information about a Series or Issue genre. - - Args: - **kwargs (Any): The keyword arguments is used for setting genre data from Metron. - - Attributes: - id (int): The Metron identification number for the genre. - name (str): The name of the genre. - """ - - def __init__(self, **kwargs): - """Initialize a new Genre.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class GenreSchema(Schema): - """Schema for the Genre.""" - - id = fields.Int() - name = fields.Str() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Genre object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Genre` object. - """ - return Genre(**data) diff --git a/mokkari/issue.py b/mokkari/issue.py deleted file mode 100644 index 4aabc27..0000000 --- a/mokkari/issue.py +++ /dev/null @@ -1,327 +0,0 @@ -""" -Issue module. - -This module provides the following classes: - -- Role -- RolesSchema -- RoleList -- Credit -- CreditsSchema -- Issue -- IssueSchema -- IssuesList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import exceptions -from mokkari.arc import ArcSchema -from mokkari.character import CharacterSchema -from mokkari.publisher import PublisherSchema -from mokkari.rating import RatingSchema -from mokkari.reprint import ReprintSchema -from mokkari.series import SeriesSchema -from mokkari.team import TeamSchema -from mokkari.variant import VariantSchema - - -class Role: - """ - The Role object contains information for creators' role. - - Args: - **kwargs (Any): The keyword arguments is used for setting role data from Metron. - - Attributes: - id (int): The Metron identification number for the role. - name (str): The name of the role. - """ - - def __init__(self, **kwargs): - """Initialize a new Role.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class RolesSchema(Schema): - """Schema for the Roles.""" - - id = fields.Int() - name = fields.Str() - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Role object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Role` object. - """ - return Role(**data) - - -class RoleList: - """ - The :obj:`RoleList` object contains a list of roles. - - Attributes: - id (int): The Metron identification number for the role. - name (str): The name of the role. - - Returns: - A list of roles. - """ - - def __init__(self, response): - """Initialize a new RoleList.""" - self.roles = [] - - schema = RolesSchema() - for role_dict in response["results"]: - try: - result = schema.load(role_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.roles.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.roles) - - def __len__(self): - """Return the length of the object.""" - return len(self.roles) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.roles[index] - - -class Credit: - """ - The Credit object contains information for creators credits for an issue. - - Args: - **kwargs (Any): The keyword arguments is used for setting credits data from Metron. - - Attributes: - id (int): The Metron identification number for the credit. - creator (str): The name of the creator. - role (RoleList): A list of roles for the creator. - """ - - def __init__(self, **kwargs): - """Initialize a new Credit.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class CreditsSchema(Schema): - """Schema for the Credits.""" - - id = fields.Int() - creator = fields.Str() - role = fields.Nested(RolesSchema, many=True) - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Credit object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Credit` object. - """ - return Credit(**data) - - -class Issue: - """ - The Issue object contains information for an issue. - - Args: - **kwargs (Any): The keyword arguments is used for setting creator data from Metron. - - Attributes: - id (int): The Metron identification number for the creator. - publisher (Publisher): The publisher information for the issue. - series (Series): The series information for the issue. - number (str): The issue number. - collection_title (str): The title of a Trade Paperback. - story_titles (list[str]): A list of stories contained in the issue. - cover_date (date): The cover date of the issue. - store_date (date, optional): The date the issue went for sale. - price (decimal): The price of the issue. - rating (Rating): The issue rating. - sku (str): Stock keeping unit for the issue. - upc (str): UPC barcode for the issue. - page_count (int): Number of pages for the issue. - desc (str): Summary description for the issue. - image (url): The url for a cover image associated with the issue. - cover_hash (str): A Perceptual hash string for the cover image. - arcs (list[:obj:`Arc`]): A list of story arcs. - credits (list[:obj:`Credit`]): A list of creator credits for the issue. - characters (list[:obj:`Character`]): A list of characters who appear in the issue. - teams (list[:obj:`Team`]): A list of teams who appear in the issue. - reprints (list[:obj:`Reprint`]): A list of reprinted issue contained in the issue. - issue_name (str): The name used to identified the issue. - variants (list[:obj:`Variant`]): A list of variant covers for the issue. - cv_id (int): Comic Vine ID for the issue. - resource_url (url): The url for the resource. - modified (datetime): The date/time the issue was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Issue.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class IssueSchema(Schema): - """ - Schema for the Issue API. - - .. versionchanged:: 0.1.6 - - - ``name`` field changed to ``story_titles`` - - ``__str__`` field change to ``issue_name`` - - .. versionchanged:: 0.2.0 - Added ``price`` and ``sku`` fields - - .. versionchanged:: 0.2.2 - Added ``upc`` field - - .. versionchanged:: 0.2.4 - - - Added ``page_count`` field - - Changed ``price`` field from a string to float value. - - .. versionchanged:: 1.0.0 - - - Changed ``price`` field to a decimal type. - - Added ``modified`` field - - .. versionchanged:: 2.1.0 - - - Add ``reprints`` field - - .. versionadded:: 2.2.2 - - - Add ``collection_title`` field - - .. versionchanged:: 2.3.0 - - - Removed ``volume`` field. The series object will have that information. - - .. versionchanged:: 2.3.2 - - - Added ``rating`` field. - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - - .. versionadded:: 2.6.0 - - - Add ``cover_hash`` field. - """ - - id = fields.Int() - publisher = fields.Nested(PublisherSchema) - series = fields.Nested(SeriesSchema) - number = fields.Str() - collection_title = fields.Str(allow_none=True, data_key="title") - story_titles = fields.List(fields.Str(allow_none=True), data_key="name") - cover_date = fields.Date() - store_date = fields.Date(allow_none=True) - price = fields.Decimal(places=2, allow_none=True) - rating = fields.Nested(RatingSchema) - sku = fields.Str() - upc = fields.Str() - page_count = fields.Int(allow_none=True, data_key="page") - desc = fields.Str(allow_none=True) - image = fields.URL(allow_none=True) - cover_hash = fields.Str() - arcs = fields.Nested(ArcSchema, many=True) - credits = fields.Nested(CreditsSchema, many=True) - characters = fields.Nested(CharacterSchema, many=True) - teams = fields.Nested(TeamSchema, many=True) - reprints = fields.Nested(ReprintSchema, many=True) - issue_name = fields.Str(data_key="issue") - variants = fields.Nested(VariantSchema, many=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the issue object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - An :obj:`Issue` object. - """ - return Issue(**data) - - -class IssuesList: - """ - The :obj:`IssuesList` object contains a list of issues. - - Attributes: - id (int): The Metron identification number for the issue. - issue (str): The name of the issue. - cover_date (date): The cover date for the issue. - modified (datetime): The date/time the creator was last changed. - - Returns: - A list of issues. - """ - - def __init__(self, response): - """Initialize a new IssuesList.""" - self.issues = [] - - schema = IssueSchema() - for issue_dict in response["results"]: - try: - result = schema.load(issue_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.issues.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.issues) - - def __len__(self): - """Return the length of the object.""" - return len(self.issues) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.issues[index] diff --git a/mokkari/publisher.py b/mokkari/publisher.py deleted file mode 100644 index e56dc96..0000000 --- a/mokkari/publisher.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -Publisher module. - -This module provides the following classes: - -- Publisher -- PublisherSchema -- PublishersList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import exceptions - - -class Publisher: - """ - The Publisher object contains information for publishers. - - Args: - **kwargs (Any): The keyword arguments is used for setting publisher data from Metron. - - Attributes: - id (int): The Metron identification number for the publisher. - name (str): The name of the publisher. - founded (int): The year the publisher was founded. - desc (str): A summary description about the publisher. - image (url): The url for an image associated with the publisher. - cv_id (int): Comic Vine ID for the publisher. - resource_url (url): The url for the resource. - modified (datetime): The date/time the publisher was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Publisher.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class PublisherSchema(Schema): - """ - Schema for the Publisher API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 2.0.2 - - - Removed ``wikipedia`` field - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - founded = fields.Int() - desc = fields.Str() - image = fields.Url(allow_none=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Publisher object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Publisher` object. - """ - return Publisher(**data) - - -class PublishersList: - """ - The :obj:`PublishersList` object contains a list of publishers. - - Attributes: - id (int): The Metron identification number for the publisher. - name (str): The name of the publisher. - modified (datetime): The date/time the publisher was last changed. - - Returns: - A list of publishers. - """ - - def __init__(self, response): - """Initialize a new PublishersList.""" - self.publishers = [] - - schema = PublisherSchema() - for pub_dict in response["results"]: - try: - result = schema.load(pub_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.publishers.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.publishers) - - def __len__(self): - """Return the length of the object.""" - return len(self.publishers) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.publishers[index] diff --git a/mokkari/rating.py b/mokkari/rating.py deleted file mode 100644 index 2829b5f..0000000 --- a/mokkari/rating.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Rating module. - -This module provides the following classes: - -- Rating -- RatingSchema -""" -from marshmallow import EXCLUDE, Schema, fields, post_load - - -class Rating: - """ - The rating object contains information for issue's rating. - - Args: - **kwargs (Any): The keyword arguments is used for setting Rating data from Metron. - - Attributes: - id (int): The Metron identification number for the rating. - name (str): The name of the rating. - """ - - def __init__(self, **kwargs): - """Initialize a Rating.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class RatingSchema(Schema): - """Schema for Ratings.""" - - id = fields.Int() - name = fields.Str() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - - @post_load - def make_object(self, data, **kwargs): - """ - Make the rating object. - - Args: - data (Any): Data from the Metron response - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj: `Rating` object. - """ - return Rating(**data) diff --git a/mokkari/reprint.py b/mokkari/reprint.py deleted file mode 100644 index 8552e54..0000000 --- a/mokkari/reprint.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Reprint module. - -This module provides the following classes: - -- Reprint -- ReprintSchema -- ReprintsList -""" -from marshmallow import EXCLUDE, Schema, fields, post_load - - -class Reprint: - """ - The Reprint object contains information for a reprint issue. - - Args: - **kwargs (Any): The keyword arguments is used for setting reprint issue - data from Metron. - - Attributes: - id (int): The Metron identification number for the issue. - issue (str): The name of the issue being reprinted. - """ - - def __init__(self, **kwargs): - """Initialize a new Reprint.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class ReprintSchema(Schema): - """Schema for the Reprint API.""" - - id = fields.Int() - issue = fields.Str() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - - @post_load - def make_object(self, data, **kwargs): - """ - Make the reprint object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Reprint` object. - """ - return Reprint(**data) diff --git a/mokkari/schemas/__init__.py b/mokkari/schemas/__init__.py new file mode 100644 index 0000000..842f8ae --- /dev/null +++ b/mokkari/schemas/__init__.py @@ -0,0 +1,20 @@ +""" +This module provides the following classes. + +- BaseModel +""" + +__all__ = ["BaseModel"] + +from pydantic import BaseModel as PydanticModel + + +class BaseModel( + PydanticModel, + populate_by_name=True, + str_strip_whitespace=True, + validate_assignment=True, + revalidate_instances="always", + extra="ignore", +): + """Base model for mokkari resources.""" diff --git a/mokkari/schemas/arc.py b/mokkari/schemas/arc.py new file mode 100644 index 0000000..575d972 --- /dev/null +++ b/mokkari/schemas/arc.py @@ -0,0 +1,46 @@ +""" +Arc module. + +This module provides the following classes: + +- Arc +- BaseArc +""" + +from datetime import datetime + +from pydantic import HttpUrl + +from mokkari.schemas import BaseModel + + +class BaseArc(BaseModel): + """ + The :obj:`BaseArc` object contains a list of story arcs. + + Attributes: + id (int): The Metron identification number for the story arc. + name (str): The name of the story arc. + modified (datetime): The date/time the story arc was last changed. + """ + + id: int + name: str + modified: datetime + + +class Arc(BaseArc): + """ + The Arc object extends :obj:`BaseArc` providing all information for a story arc. + + Attributes: + desc (str): The description of the story arc. + image (HttpUrl): The url for an image associated with the story arc. + cv_id (int): Comic Vine ID for the story arc. + resource_url (HttpUrl): The url for the resource. + """ + + desc: str | None = None + image: HttpUrl | None = None + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/character.py b/mokkari/schemas/character.py new file mode 100644 index 0000000..9b8ba44 --- /dev/null +++ b/mokkari/schemas/character.py @@ -0,0 +1,54 @@ +""" +Character module. + +This module provides the following classes: + +- BaseCharacter +- Character +""" + +from datetime import datetime + +from pydantic import HttpUrl + +from mokkari.schemas import BaseModel +from mokkari.schemas.creator import BaseCreator +from mokkari.schemas.team import BaseTeam + + +class BaseCharacter(BaseModel): + """ + The :obj:`BaseCharacter` object contains a list of characters. + + Attributes: + id (int): The Metron identification number for the character. + name (str): The name of the character. + modified (datetime): The date/time the team was last changed. + """ + + id: int + name: str + modified: datetime + + +class Character(BaseCharacter): + """ + The Character object extends :obj:`BaseCharacter` providing all information for a character. + + Attributes: + alias (list[str]): The alias of the character. + desc (str): The description of the character. + image (url): The url for an image associated with the character. + creators (list[:obj:`Generic`]): A list of creators for the character. + teams (list[:obj:`Generic`]): A list of teams for the character. + cv_id (int): Comic Vine ID for the character. + resource_url (url): The url for the resource. + """ + + alias: list[str] | None = None + desc: str | None = None + image: HttpUrl | None = None + creators: list[BaseCreator] = [] + teams: list[BaseTeam] = [] + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/creator.py b/mokkari/schemas/creator.py new file mode 100644 index 0000000..c6aa807 --- /dev/null +++ b/mokkari/schemas/creator.py @@ -0,0 +1,52 @@ +""" +Creator module. + +This module provides the following classes: + +- BaseCreator +- Creator +""" + +from datetime import date, datetime + +from pydantic import HttpUrl, PastDate + +from mokkari.schemas import BaseModel + + +class BaseCreator(BaseModel): + """ + The :obj:`BaseCreator` object contains a list of creators. + + Attributes: + id (int): The Metron identification number for the creator. + name (str): The name of the creator. + modified (datetime): The date/time the team was last changed. + """ + + id: int + name: str + modified: datetime + + +class Creator(BaseCreator): + """ + The Creator object extends :obj:`BaseCreator` providing all information for a creator. + + Attributes: + birth (date): The date of birth for the creator. + death (date): The date of death for the creator. + desc (str): The description of the creator. + image (HttpUrl): The url for an image associated with the creator. + alias (list[str]): The alias of the creator. + cv_id (int): Comic Vine ID for the creator. + resource_url (HttpUrl): The url for the resource. + """ + + birth: PastDate | None = None + death: date | None = None + desc: str | None = None + image: HttpUrl | None = None + alias: list[str] | None = None + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/generic.py b/mokkari/schemas/generic.py new file mode 100644 index 0000000..84bf533 --- /dev/null +++ b/mokkari/schemas/generic.py @@ -0,0 +1,22 @@ +""" +Generic module. + +This module provides the following classes: + +- GenericItem +""" + +from mokkari.schemas import BaseModel + + +class GenericItem(BaseModel): + """ + The :obj:`GenericItem` object contains basic information for various resources. + + Attributes: + id (int): The id of the item. + name (str): The name of the item. + """ + + id: int + name: str diff --git a/mokkari/schemas/issue.py b/mokkari/schemas/issue.py new file mode 100644 index 0000000..5e45d13 --- /dev/null +++ b/mokkari/schemas/issue.py @@ -0,0 +1,161 @@ +""" +Issue module. + +This module provides the following classes: + +- Credit +- BasicSeries +- IssueSeries +- CommonIssue +- BaseIssue +- Issue +""" + +from datetime import date, datetime +from decimal import Decimal + +from pydantic import Field, HttpUrl + +from mokkari.schemas import BaseModel +from mokkari.schemas.arc import BaseArc +from mokkari.schemas.character import BaseCharacter +from mokkari.schemas.generic import GenericItem +from mokkari.schemas.reprint import Reprint +from mokkari.schemas.team import BaseTeam +from mokkari.schemas.variant import Variant + + +class Credit(BaseModel): + """ + The :obj:`Credit` object contains information about an issue creator credits. + + Attributes: + id (int): The Metron identification number for the issue credit. + creator (str): The name of the creator for the issue credit. + role (list[GenericItem]): The role of the creator for the issue. + """ + + id: int + creator: str + role: list[GenericItem] = [] + + +class BasicSeries(BaseModel): + """ + The :obj:`BasicSeries` object contains basic series information for an issue. + + Attributes: + name (str): The name of the series. + volume (int): The volume of the series. + year_began (int): The year the series began. + """ + + name: str + volume: int + year_began: int + + +class IssueSeries(BaseModel): + """ + The :obj:`AssociatedSeries` object contains more detailed series information. + + Attributes: + id (int): The Metron identification number for series. + name (str): The name of the series. + sort_name (str): The sort name of the series. + volume (int): The volume of the series. + series_type (GenericItem): The type of series. + genres (list[Generic]): The genres of the series. + """ + + id: int + name: str + sort_name: str + volume: int + series_type: GenericItem + genres: list[GenericItem] = [] + + +class CommonIssue(BaseModel): + """ + The :obj:`CommonIssue` object contains common information for BaseIssue and Issue objects. + + Attributes: + id (int): The Metron identification number for the associated series. + number (str): The number of the issue. + cover_date (date): The cover date of the issue. + image (HttpUrl): The url of the cover image for the issue. + cover_hash (str): The hash of the cover image for the issue. + modified (datetime): The modified date of the issue. + """ + + id: int + number: str + cover_date: date + image: HttpUrl | None = None + cover_hash: str | None = None + modified: datetime + + +class BaseIssue(CommonIssue): + """ + The :obj:`BaseIssue` object extends the :obj:`CommonIssue` object. + + Attributes: + issue_name (str): The name of the issue. + series (BasicSeries): The series for the issue. + """ + + issue_name: str = Field(alias="issue") + series: BasicSeries + + +class Issue(CommonIssue): + """ + The :obj:`Issue` object extends the :obj:`CommonIssue` object with all the info for an issue. + + Attributes: + publisher (GenericPublisher): The publisher of the issue. + series (IssueSeries): The series for the issue. + collection_title (str): The collection title of the issue. Normally only used with TPB. + story_titles (list[str]): A list of stories contained in the issue. + cover_date (date): The cover date of the issue. + store_date (date): The store date of the issue. + price (Decimal): The price of the issue. + rating (GenericItem): The rating of the issue. + sku (str): The sku of the issue. + isbn (str): The isbn of the issue. + upc (str): The upc of the issue. + page_count (int): The number of pages of the issue. + desc (str): The description of the issue. + arcs (list[BaseArc]): A list of story arcs for the issue. + credits (list[Credit]): A list of creator credits for the issue. + characters (list[BaseCharacter]): A list of characters for the issue. + teams (list[BaseTeam]): A list of teams for the issue. + reprints (list[Reprint]): A list of issues printed, + variants (list[Variant]): A list of variant covers for the issue. + cv_id (int): The Comic Vine ID of the issue. + resource_url (HttpUrl): The URL of the issue. + """ + + publisher: GenericItem + series: IssueSeries + collection_title: str = Field(alias="title") + story_titles: list[str] = Field(alias="name") + cover_date: date + store_date: date | None = None + price: Decimal | None = None + rating: GenericItem + sku: str | None = None + isbn: str | None = None + upc: str | None = None + page_count: int | None = Field(alias="page", default=None) + desc: str | None = None + arcs: list[BaseArc] = [] + credits: list[Credit] = [] + characters: list[BaseCharacter] = [] + teams: list[BaseTeam] = [] + reprints: list[Reprint] = [] + variants: list[Variant] = [] + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/publisher.py b/mokkari/schemas/publisher.py new file mode 100644 index 0000000..a96d748 --- /dev/null +++ b/mokkari/schemas/publisher.py @@ -0,0 +1,48 @@ +""" +Publisher module. + +This module provides the following classes: + +- BasePublisher +- Publisher +""" + +from datetime import datetime + +from pydantic import HttpUrl + +from mokkari.schemas import BaseModel + + +class BasePublisher(BaseModel): + """ + The :obj:`BasePublisher` object contains a list of publishers. + + Attributes: + id (int): The Metron identification number for the publisher. + name (str): The name of the publisher. + modified (datetime): The date/time the team was last changed. + """ + + id: int + name: str + modified: datetime + + +class Publisher(BasePublisher): + """ + The Publisher object extends :obj:`BasePublisher` providing all information for a publisher. + + Attributes: + founded (int): The year the publisher was founded. + desc (str): The description of the publisher. + image (HttpUrl): The url for an image associated with the publisher. + cv_id (int): Comic Vine ID for the publisher. + resource_url (HttpUrl): The url for the resource. + """ + + founded: int | None = None + desc: str | None = None + image: HttpUrl | None = None + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/reprint.py b/mokkari/schemas/reprint.py new file mode 100644 index 0000000..52d394e --- /dev/null +++ b/mokkari/schemas/reprint.py @@ -0,0 +1,22 @@ +""" +Reprint module. + +This module provides the following classes: + +- Reprint +""" + +from mokkari.schemas import BaseModel + + +class Reprint(BaseModel): + """ + The :obj:`Reprint` object contains a list of reprinted issues. + + Attributes: + id (int): The Metron identification number for the team. + issue (str): The name of the issue. + """ + + id: int + issue: str diff --git a/mokkari/schemas/series.py b/mokkari/schemas/series.py new file mode 100644 index 0000000..be25454 --- /dev/null +++ b/mokkari/schemas/series.py @@ -0,0 +1,89 @@ +""" +Series module. + +This module provides the following classes: + +- AssociatedSeries +- CommonSeries +- BaseSeries +- Series +""" + +from datetime import datetime + +from pydantic import Field, HttpUrl + +from mokkari.schemas import BaseModel +from mokkari.schemas.generic import GenericItem + + +class AssociatedSeries(BaseModel): + """ + The :obj:`AssociatedSeries` object contains information about an associated series. + + Attributes: + id (int): The Metron identification number for the associated series. + name (str): The name of the associated series. + """ + + id: int + name: str = Field(alias="series") + + +class CommonSeries(BaseModel): + """ + The :obj:`CommonSeries` contains fields common to :obj:`BaseSeries` & :obj:`Series` objects. + + Attributes: + id (int): The Metron identification number for the series. + year_began (int): The year the series began. + issue_count (int): The number of issues. + modified (datetime): The date/time the series was last changed. + """ + + id: int + year_began: int + issue_count: int + modified: datetime + + +class BaseSeries(CommonSeries): + """ + The :obj:`BaseSeries` object contains extend the :obj:`CommonSeries`. + + Attributes: + display_name (str): The name of the series. + """ + + display_name: str = Field(alias="series") + + +class Series(CommonSeries): + """ + :obj:`Series` extends :obj:`CommonSeries` and contains all information about a series. + + Attributes: + name (str): The name of the series. + sort_name (str): The name used to determine the sort order for a series. + volume (int): The volume of the series. + series_type (GenericItem): The type of series. + publisher (GenericItem): The publisher of the series. + year_end (int): The year the series ended. + desc (str): The description of the series. + genres list(Generic): The genres of the series. + associated list(AssociatedSeries): The series associated with the series. + cv_id (int): The Comic Vine ID of the series. + resource_url (HttpUrl): The URL of the series + """ + + name: str + sort_name: str + volume: int + series_type: GenericItem + publisher: GenericItem + year_end: int | None = None + desc: str | None = None + genres: list[GenericItem] = [] + associated: list[AssociatedSeries] = [] + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/team.py b/mokkari/schemas/team.py new file mode 100644 index 0000000..edcaa2d --- /dev/null +++ b/mokkari/schemas/team.py @@ -0,0 +1,52 @@ +""" +Team module. + +This module provides the following classes: + +- BaseTeam +- Team +""" + +from datetime import datetime + +from pydantic import HttpUrl + +from mokkari.schemas import BaseModel +from mokkari.schemas.generic import GenericItem + + +class BaseTeam(BaseModel): + """ + The :obj:`BaseTeam` object contains a list of teams. + + Attributes: + id (int): The Metron identification number for the team. + name (str): The name of the team. + modified (datetime): The date/time the team was last changed. + + Returns: + A list of teams. + """ + + id: int + name: str + modified: datetime + + +class Team(BaseTeam): + """ + The Team object extends the :obj:`BaseTeam` by containing all information for a team. + + Attributes: + desc (str): The description of the team. + image (url): The url for an image associated with the team. + creators (list[:obj:`Generic`]): A list of creators for the team. + cv_id (int): Comic Vine ID for the team. + resource_url (url): The url for the resource. + """ + + desc: str | None = None + image: HttpUrl | None = None + creators: list[GenericItem] = [] + cv_id: int | None = None + resource_url: HttpUrl diff --git a/mokkari/schemas/variant.py b/mokkari/schemas/variant.py new file mode 100644 index 0000000..56822b1 --- /dev/null +++ b/mokkari/schemas/variant.py @@ -0,0 +1,28 @@ +""" +Variant module. + +This module provides the following classes: + +- Variant +""" + +from pydantic import HttpUrl + +from mokkari.schemas import BaseModel + + +class Variant(BaseModel): + """ + The :obj:`Variant` object contains information about a variant cover.. + + Attributes: + name (str): The name of the variant cover. + sku (str): The sku of the variant cover. + upc (str): The upc of the variant cover. + image (HttpUrl): The url for the variant cover. + """ + + name: str | None = None + sku: str | None = None + upc: str | None = None + image: HttpUrl diff --git a/mokkari/series.py b/mokkari/series.py deleted file mode 100644 index a86b608..0000000 --- a/mokkari/series.py +++ /dev/null @@ -1,277 +0,0 @@ -""" -Series module. - -This module provides the following classes: - -- SeriesType -- SeriesTypeSchema -- Series -- SeriesSchema -- SeriesList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import exceptions -from mokkari.genre import GenreSchema -from mokkari.publisher import PublisherSchema - - -class SeriesType: - """ - The SeriesType object contains information for type of series. - - Args: - **kwargs (Any): The keyword arguments is used for setting series type data from Metron. - - Attributes: - id (int): The Metron identification number for the series type. - name (str): The name of the series type. - """ - - def __init__(self, **kwargs): - """Initialize a new SeriesType.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class SeriesTypeSchema(Schema): - """Schema for the Series Type.""" - - id = fields.Int() - name = fields.Str() - - @post_load - def make_object(self, data, **kwargs): - """ - Make the SeriesType object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`SeriesType` object. - """ - return SeriesType(**data) - - -class SeriesTypeList: - """ - The :obj:`SeriesTypeList` object contains a list of series types. - - Attributes: - id (int): The Metron identification number for the series type. - name (str): The name of the series type. - - Returns: - A list of series types. - """ - - def __init__(self, response): - """Initialize a new SeriesTypeList.""" - self.series = [] - - schema = SeriesTypeSchema() - for series_type_dict in response["results"]: - try: - result = schema.load(series_type_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.series.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.series) - - def __len__(self): - """Return the length of the object.""" - return len(self.series) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.series[index] - - -class AssociatedSeries: - """ - The AssociateSeries objects contains any associated series to the primary series. - - Args: - **kwargs (Any): The keyword arguments is used for setting associated series data - from Metron. - - Attributes: - id (int): The Metron identification number for the associated series. - name (str): The name of the associated series. - """ - - def __init__(self, **kwargs): - """Initialize a new AssociatedSeries.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class AssociatedSeriesSchema(Schema): - """Schema for Associated Series.""" - - id = fields.Int() - name = fields.Str(data_key="series") - - @post_load - def make_object(self, data, **kwargs): - """ - Make the AssociatedSeries object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - An :obj:`AssociatedSeries` object. - """ - return AssociatedSeries(**data) - - -class Series: - """ - The Series object contains information for comic series. - - Args: - **kwargs (Any): The keyword arguments is used for setting series data from Metron. - - Attributes: - id (int): The Metron identification number for the series. - name (str): The name of the series. - sort_name (str): The name used to sort a series. - volume (int): The volume number for a series. - publisher (Publisher): The publisher of the series. - year_began (int): The cover year the series began. - year_end (int, optional): The cover year in which the series ended. - desc (str): A summary description of the series. - issue_count (int): The number of issues the series contains. - display_name (str): The display name for the series. - genres (list[Genre]): A list of genres for the series. - associated (list[AssociatedSeries]): A list of of series associated with the - primary series. - cv_id (int): Comic Vine ID for the series. - resource_url (url): The url for the resource. - modified (datetime): The date/time the series was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Series.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class SeriesSchema(Schema): - """ - Schema for the Series API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 1.0.5 - - - Added ``associated`` field - - .. versionchanged:: 2.0.0 - - - Changed ``publisher`` to a nested field. - - .. versionchanged:: 2.0.3 - - Changed ``series_type`` to a string field. - - .. versionchanged:: 2.0.4 - - Reverted ``series_type`` back to a nested field. - - .. versionchanged:: 2.1.1 - Added ``genres`` fields - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - sort_name = fields.Str() - volume = fields.Int() - series_type = fields.Nested(SeriesTypeSchema) - publisher = fields.Nested(PublisherSchema) - year_began = fields.Int() - year_end = fields.Int(allow_none=True) - desc = fields.Str() - issue_count = fields.Int() - image = fields.Url(allow_none=True) - display_name = fields.Str(data_key="series") - genres = fields.Nested(GenreSchema, many=True) - associated = fields.Nested(AssociatedSeriesSchema, many=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Series object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Series` object. - """ - return Series(**data) - - -class SeriesList: - """ - The :obj:`SeriesList` object contains a list of series. - - Attributes: - id (int): The Metron identification number for the series. - series (str): The name of the series. - modified (datetime): The date/time the series was last changed. - - Returns: - A list of series. - """ - - def __init__(self, response): - """Initialize a new SeriesList.""" - self.series = [] - - schema = SeriesSchema() - for series_dict in response["results"]: - try: - result = schema.load(series_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.series.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.series) - - def __len__(self): - """Return the length of the object.""" - return len(self.series) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.series[index] diff --git a/mokkari/session.py b/mokkari/session.py index e953211..d4fe0e1 100644 --- a/mokkari/session.py +++ b/mokkari/session.py @@ -5,28 +5,28 @@ - Session """ + import platform from collections import OrderedDict -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from urllib.parse import urlencode import requests -from marshmallow import ValidationError +from pydantic import TypeAdapter, ValidationError from ratelimit import limits, sleep_and_retry from requests.adapters import HTTPAdapter from urllib3 import Retry # Alias these modules to prevent namespace collision with methods. -from mokkari import __version__ -from mokkari import arc as arcs -from mokkari import character as characters -from mokkari import creator as creators -from mokkari import exceptions -from mokkari import issue as issues -from mokkari import publisher as publishers -from mokkari import series as ser -from mokkari import sqlite_cache -from mokkari import team as teams +from mokkari import __version__, exceptions, sqlite_cache +from mokkari.schemas.arc import Arc, BaseArc +from mokkari.schemas.character import BaseCharacter, Character +from mokkari.schemas.creator import BaseCreator, Creator +from mokkari.schemas.generic import GenericItem +from mokkari.schemas.issue import BaseIssue, Issue +from mokkari.schemas.publisher import BasePublisher, Publisher +from mokkari.schemas.series import BaseSeries, Series +from mokkari.schemas.team import BaseTeam, Team ONE_MINUTE = 60 @@ -61,9 +61,9 @@ def __init__( def _call( self, - endpoint: List[Union[str, int]], - params: Optional[Dict[str, Union[str, int]]] = None, - ) -> Dict[str, Any]: + endpoint: list[Union[str, int]], + params: Optional[dict[str, Union[str, int]]] = None, + ) -> dict[str, Any]: """ Make request for api endpoints. @@ -95,7 +95,7 @@ def _call( return data - def creator(self, _id: int) -> creators.Creator: + def creator(self, _id: int) -> Creator: """ Request data for a creator based on its ``_id``. @@ -108,16 +108,17 @@ def creator(self, _id: int) -> creators.Creator: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["creator", _id]) + adaptor = TypeAdapter(Creator) try: - result = creators.CreatorSchema().load(self._call(["creator", _id])) + result = adaptor.validate_python(resp) except ValidationError as error: raise exceptions.ApiError(error) from error - return result def creators_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> creators.CreatorsList: + self, params: Optional[dict[str, Union[str, int]]] = None + ) -> list[BaseCreator]: """ Request a list of creators. @@ -127,10 +128,15 @@ def creators_list( Returns: A :obj:`CreatorsList` object. """ - res = self._get_results(["creator"], params) - return creators.CreatorsList(res) + resp = self._get_results(["creator"], params) + adaptor = TypeAdapter(list[BaseCreator]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as error: + raise exceptions.ApiError(error) from error + return result - def character(self, _id: int) -> characters.Character: + def character(self, _id: int) -> Character: """ Request data for a character based on its ``_id``. @@ -143,16 +149,17 @@ def character(self, _id: int) -> characters.Character: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["character", _id]) + adaptor = TypeAdapter(Character) try: - result = characters.CharacterSchema().load(self._call(["character", _id])) + result = adaptor.validate_python(resp) except ValidationError as error: raise exceptions.ApiError(error) from error - return result def characters_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> characters.CharactersList: + self, params: Optional[dict[str, Union[str, int]]] = None + ) -> list[BaseCharacter]: """ Request a list of characters. @@ -162,10 +169,15 @@ def characters_list( Returns: A :class:`CharactersList` object. """ - res = self._get_results(["character"], params) - return characters.CharactersList(res) + resp = self._get_results(["character"], params) + adaptor = TypeAdapter(list[BaseCharacter]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as error: + raise exceptions.ApiError(error) from error + return result - def character_issues_list(self, _id: int) -> List[issues.Issue]: + def character_issues_list(self, _id: int) -> list[BaseIssue]: """ Request a list of issues that a character appears in. @@ -177,10 +189,15 @@ def character_issues_list(self, _id: int) -> List[issues.Issue]: Returns: A list of :class:`Issue` objects. """ - result = self._get_results(["character", _id, "issue_list"]) - return issues.IssuesList(result) + resp = self._get_results(["character", _id, "issue_list"]) + adaptor = TypeAdapter(list[BaseIssue]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def publisher(self, _id: int) -> publishers.Publisher: + def publisher(self, _id: int) -> Publisher: """ Request data for a publisher based on its ``_id``. @@ -193,16 +210,17 @@ def publisher(self, _id: int) -> publishers.Publisher: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["publisher", _id]) + adaptor = TypeAdapter(Publisher) try: - result = publishers.PublisherSchema().load(self._call(["publisher", _id])) - except ValidationError as error: - raise exceptions.ApiError(error) from error - + result = adaptor.validate_python(resp) + except ValidationError as err: + raise exceptions.ApiError(err) from err return result def publishers_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> publishers.PublishersList: + self, params: Optional[dict[str, Union[str, int]]] = None + ) -> list[BasePublisher]: """ Request a list of publishers. @@ -212,10 +230,15 @@ def publishers_list( Returns: A :class:`PublishersList` object. """ - res = self._get_results(["publisher"], params) - return publishers.PublishersList(res) + resp = self._get_results(["publisher"], params) + adapter = TypeAdapter(list[BasePublisher]) + try: + result = adapter.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def team(self, _id: int) -> teams.Team: + def team(self, _id: int) -> Team: """ Request data for a team based on its ``_id``. @@ -228,16 +251,15 @@ def team(self, _id: int) -> teams.Team: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["team", _id]) + adaptor = TypeAdapter(Team) try: - result = teams.TeamSchema().load(self._call(["team", _id])) + result = adaptor.validate_python(resp) except ValidationError as error: raise exceptions.ApiError(error) from error - return result - def teams_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> teams.TeamsList: + def teams_list(self, params: Optional[dict[str, Union[str, int]]] = None) -> list[BaseTeam]: """ Request a list of teams. @@ -247,10 +269,15 @@ def teams_list( Returns: A :class:`TeamsList` object. """ - res = self._get_results(["team"], params) - return teams.TeamsList(res) + resp = self._get_results(["team"], params) + adapter = TypeAdapter(list[BaseTeam]) + try: + result = adapter.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def team_issues_list(self, _id: int) -> List[issues.Issue]: + def team_issues_list(self, _id: int) -> list[BaseIssue]: """ Request a list of issues that a team appears in. @@ -262,10 +289,15 @@ def team_issues_list(self, _id: int) -> List[issues.Issue]: Returns: A list of :class:`Issue` objects. """ - result = self._get_results(["team", _id, "issue_list"]) - return issues.IssuesList(result) + resp = self._get_results(["team", _id, "issue_list"]) + adapter = TypeAdapter(list[BaseIssue]) + try: + result = adapter.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def arc(self, _id: int) -> arcs.Arc: + def arc(self, _id: int) -> Arc: """ Request data for a story arc based on its ``_id``. @@ -278,14 +310,15 @@ def arc(self, _id: int) -> arcs.Arc: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["arc", _id]) + adaptor = TypeAdapter(Arc) try: - result = arcs.ArcSchema().load(self._call(["arc", _id])) - except ValidationError as error: - raise exceptions.ApiError(error) from error - + result = adaptor.validate_python(resp) + except ValidationError as err: + raise exceptions.ApiError(err) from err return result - def arcs_list(self, params: Optional[Dict[str, Union[str, int]]] = None) -> arcs.ArcsList: + def arcs_list(self, params: Optional[dict[str, Union[str, int]]] = None) -> list[BaseArc]: """ Request a list of story arcs. @@ -295,10 +328,15 @@ def arcs_list(self, params: Optional[Dict[str, Union[str, int]]] = None) -> arcs Returns: A :class:`ArcsList` object. """ - res = self._get_results(["arc"], params) - return arcs.ArcsList(res) + resp = self._get_results(["arc"], params) + adapter = TypeAdapter(list[BaseArc]) + try: + result = adapter.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def arc_issues_list(self, _id: int) -> List[issues.Issue]: + def arc_issues_list(self, _id: int) -> list[BaseIssue]: """ Request a list of issues for a story arc. @@ -308,10 +346,15 @@ def arc_issues_list(self, _id: int) -> List[issues.Issue]: Returns: A list of :class:`Issue` objects. """ - result = self._get_results(["arc", _id, "issue_list"]) - return issues.IssuesList(result) + resp = self._get_results(["arc", _id, "issue_list"]) + adaptor = TypeAdapter(list[BaseIssue]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def series(self, _id: int) -> ser.Series: + def series(self, _id: int) -> Series: """ Request data for a series based on its ``_id``. @@ -324,16 +367,15 @@ def series(self, _id: int) -> ser.Series: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["series", _id]) + adaptor = TypeAdapter(Series) try: - result = ser.SeriesSchema().load(self._call(["series", _id])) - except ValidationError as error: - raise exceptions.ApiError(error) from error - + result = adaptor.validate_python(resp) + except ValidationError as err: + raise exceptions.ApiError(err) from err return result - def series_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> ser.SeriesList: + def series_list(self, params: Optional[dict[str, Union[str, int]]] = None) -> list[BaseSeries]: """ Request a list of series. @@ -343,12 +385,17 @@ def series_list( Returns: A :class:`SeriesList` object. """ - res = self._get_results(["series"], params) - return ser.SeriesList(res) + resp = self._get_results(["series"], params) + adaptor = TypeAdapter(list[BaseSeries]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result def series_type_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> ser.SeriesTypeList: + self, params: Optional[dict[str, Union[str, int]]] = None + ) -> list[GenericItem]: """ Request a list of series types. @@ -362,10 +409,15 @@ def series_type_list( Returns: A :class:`SeriesTypeList` object. """ - res = self._get_results(["series_type"], params) - return ser.SeriesTypeList(res) + resp = self._get_results(["series_type"], params) + adaptor = TypeAdapter(list[GenericItem]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def issue(self, _id: int) -> issues.Issue: + def issue(self, _id: int) -> Issue: """ Request data for an issue based on it's ``_id``. @@ -378,16 +430,15 @@ def issue(self, _id: int) -> issues.Issue: Raises: ApiError: If there is a problem with the API request. """ + resp = self._call(["issue", _id]) + adaptor = TypeAdapter(Issue) try: - result = issues.IssueSchema().load(self._call(["issue", _id])) + result = adaptor.validate_python(resp) except ValidationError as error: raise exceptions.ApiError(error) from error - return result - def issues_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> issues.IssuesList: + def issues_list(self, params: Optional[dict[str, Union[str, int]]] = None) -> list[BaseIssue]: """ Request a list of issues. @@ -397,12 +448,15 @@ def issues_list( Returns: A :class:`IssuesList` object. """ - res = self._get_results(["issue"], params) - return issues.IssuesList(res) + resp = self._get_results(["issue"], params) + adaptor = TypeAdapter(list[BaseIssue]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result - def role_list( - self, params: Optional[Dict[str, Union[str, int]]] = None - ) -> issues.RoleList: + def role_list(self, params: Optional[dict[str, Union[str, int]]] = None) -> list[GenericItem]: """ Request a list of creator roles. @@ -413,14 +467,19 @@ def role_list( A :class:`RoleList` object. """ - res = self._get_results(["role"], params) - return issues.RoleList(res) + resp = self._get_results(["role"], params) + adaptor = TypeAdapter(list[GenericItem]) + try: + result = adaptor.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result def _get_results( self, - endpoint: List[Union[str, int]], - params: Optional[Dict[str, Union[str, int]]] = None, - ) -> Dict[str, Any]: + endpoint: list[Union[str, int]], + params: Optional[dict[str, Union[str, int]]] = None, + ) -> dict[str, Any]: if params is None: params = {} @@ -429,7 +488,7 @@ def _get_results( result = self._retrieve_all_results(result) return result - def _retrieve_all_results(self, data: Dict[str, Any]) -> Dict[str, Any]: + def _retrieve_all_results(self, data: dict[str, Any]) -> dict[str, Any]: has_next_page = True next_page = data["next"] @@ -456,9 +515,7 @@ def _retrieve_all_results(self, data: Dict[str, Any]) -> Dict[str, Any]: @sleep_and_retry @limits(calls=25, period=ONE_MINUTE) - def _request_data( - self, url: str, params: Optional[Dict[str, Union[str, int]]] = None - ) -> Any: + def _request_data(self, url: str, params: Optional[dict[str, Union[str, int]]] = None) -> Any: if params is None: params = {} diff --git a/mokkari/sqlite_cache.py b/mokkari/sqlite_cache.py index be18ab8..e444e0e 100644 --- a/mokkari/sqlite_cache.py +++ b/mokkari/sqlite_cache.py @@ -5,6 +5,7 @@ - SqliteCache """ + import json import sqlite3 from datetime import datetime, timedelta @@ -21,9 +22,7 @@ class SqliteCache: before they expire. """ - def __init__( - self, db_name: str = "mokkari_cache.db", expire: Optional[int] = None - ) -> None: + def __init__(self, db_name: str = "mokkari_cache.db", expire: Optional[int] = None) -> None: """Initialize a new SqliteCache.""" self.expire = expire self.con = sqlite3.connect(db_name) diff --git a/mokkari/team.py b/mokkari/team.py deleted file mode 100644 index 7b6ec58..0000000 --- a/mokkari/team.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Team module. - -This module provides the following classes: - -- Team -- TeamSchema -- TeamsList -""" -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load - -from mokkari import creator, exceptions - - -class Team: - """ - The Team object contains information for teams. - - Args: - **kwargs (Any): The keyword arguments is used for setting team data from Metron. - - Attributes: - id (int): The Metron identification number for the team. - name (str): The name of the team. - desc (str): The description of the team. - image (url): The url for an image associated with the team. - creators (list[:obj:`Creator`]): A list of creators for the team. - cv_id (int): Comic Vine ID for the team. - resource_url (url): The url for the resource. - modified (datetime): The date/time the team was last changed. - """ - - def __init__(self, **kwargs): - """Initialize a new Team.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class TeamSchema(Schema): - """ - Schema for the Team API. - - .. versionchanged:: 1.0.0 - - - Added ``modified`` field - - .. versionchanged:: 2.0.2 - - Removed ``wikipedia`` field - - .. versionchanged:: 2.3.3 - - - Added ``resource_url`` field. - - .. versionadded:: 2.4.0 - - - Added ``cv_id`` field. - """ - - id = fields.Int() - name = fields.Str() - desc = fields.Str() - image = fields.Url(allow_none=True) - creators = fields.Nested(creator.CreatorSchema, many=True) - cv_id = fields.Int(allow_none=True) - resource_url = fields.URL() - modified = fields.DateTime() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - datetime = "%Y-%m-%dT%H:%M:%S%z" - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Team object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - A :obj:`Team` object - """ - return Team(**data) - - -class TeamsList: - """ - The :obj:`TeamsList` object contains a list of teams. - - Attributes: - id (int): The Metron identification number for the team. - name (str): The name of the team. - modified (datetime): The date/time the team was last changed. - - Returns: - A list of teams. - """ - - def __init__(self, response): - """Initialize a new TeamsList.""" - self.teams = [] - - schema = TeamSchema() - for team_dict in response["results"]: - try: - result = schema.load(team_dict) - except ValidationError as error: - raise exceptions.ApiError(error) from error - - self.teams.append(result) - - def __iter__(self): - """Return an iterator object.""" - return iter(self.teams) - - def __len__(self): - """Return the length of the object.""" - return len(self.teams) - - def __getitem__(self, index: int): - """Return the object of a at index.""" - return self.teams[index] diff --git a/mokkari/variant.py b/mokkari/variant.py deleted file mode 100644 index 4067852..0000000 --- a/mokkari/variant.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Variant module. - -This module provides the following classes: - -- Variant -- VariantSchema -""" -from marshmallow import EXCLUDE, Schema, fields, post_load - - -class Variant: - """ - The Variant object contains information for variant issues. - - Args: - **kwargs (Any): The keyword arguments is used for setting arc data from Metron. - - Attributes: - name (int): The name of the variant cover. - sku (str): The stock keeping unit for the variant cover. - image (url): The url for an image for the variant cover. - """ - - def __init__(self, **kwargs): - """Initialize a new Variant.""" - for k, v in kwargs.items(): - setattr(self, k, v) - - -class VariantSchema(Schema): - """Schema for the Variant API.""" - - name = fields.Str() - sku = fields.Str() - image = fields.Url() - - class Meta: - """Any unknown fields will be excluded.""" - - unknown = EXCLUDE - - @post_load - def make_object(self, data, **kwargs): - """ - Make the Variant object. - - Args: - data (Any): Data from Metron response. - **kwargs (Any): Any additional keyword arguments. - - Returns: - An :obj:`Variant` object - """ - return Variant(**data) diff --git a/poetry.lock b/poetry.lock index 9cbd5e8..67c7edd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,6 +11,17 @@ files = [ {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + [[package]] name = "aspy-refactor-imports" version = "3.0.2" @@ -252,63 +263,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.dependencies] @@ -486,25 +497,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -616,26 +608,6 @@ files = [ {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] -[[package]] -name = "marshmallow" -version = "3.20.2" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -optional = false -python-versions = ">=3.8" -files = [ - {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, - {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, -] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["pre-commit (>=2.4,<4.0)"] -tests = ["pytest", "pytz", "simplejson"] - [[package]] name = "mccabe" version = "0.7.0" @@ -767,6 +739,142 @@ files = [ {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, ] +[[package]] +name = "pydantic" +version = "2.5.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, + {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.14.6" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.14.6" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, + {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, + {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, + {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, + {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, + {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, + {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, + {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, + {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, + {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, + {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, + {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, + {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, + {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, + {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, + {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pydocstyle" version = "6.3.0" @@ -1094,7 +1202,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" @@ -1350,22 +1457,7 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "37ffd05a8c29201fb78aa030f16030e15cc0b07f9a7c1d105240a7df7652a53c" +python-versions = "^3.10" +content-hash = "7e1e331b3701d973569ce02d1e29cdf1294d80c88d53e0c94bf981baf9b09e88" diff --git a/pyproject.toml b/pyproject.toml index c05c921..b4ee993 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,9 +18,9 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Natural Language :: English", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Internet", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX", @@ -31,10 +31,10 @@ classifiers = [ keywords=["comics", "comic", "metadata"] [tool.poetry.dependencies] -python = "^3.9" -marshmallow = "^3.13.0" +python = "^3.10" requests = "^2.26.0" ratelimit = "^2.2.1" +pydantic = "^2.5.3" [tool.poetry.group.dev.dependencies] pytest = "^7.4.2" @@ -64,16 +64,16 @@ requires = ["poetry-core>=1.1.0"] build-backend = "poetry.core.masonry.api" [tool.black] -line-length = 95 -target-version = ['py39'] +line-length = 100 +target-version = ['py310'] [tool.isort] profile = "black" multi_line_output = 3 -line_length = 95 +line_length = 100 default_section = "THIRDPARTY" known_first_party = [] -known_third_party = ["marshmallow", "pytest", "ratelimit", "requests", "requests_mock", "urllib3"] +known_third_party = ["pydantic", "pytest", "ratelimit", "requests", "requests_mock", "urllib3"] [tool.poetry.urls] "Homepage" = "https://github.com/Metron-Project/mokkari" diff --git a/tests/conftest.py b/tests/conftest.py index 9227cdb..a03e60a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ This module contains pytest fixtures. """ + import os import pytest diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 8591a34..c6491ee 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -3,46 +3,48 @@ This module contains tests for Arc objects. """ + import json from datetime import date, datetime, timedelta, timezone import pytest import requests_mock -from mokkari import arc, exceptions +from mokkari import exceptions from mokkari.session import Session def test_known_arc(talker: Session) -> None: """Test for known arcs.""" - heroes: arc.ArcSchema = talker.arc(1) - assert heroes.name == "Heroes In Crisis" + witching = talker.arc(2) + assert witching.name == "The Witching Hour" assert ( - heroes.image - == "https://static.metron.cloud/media/arc/2018/11/12/heroes-in-crisis.jpeg" + witching.image.__str__() + == "https://static.metron.cloud/media/arc/2018/11/13/witching-hour.jpg" ) - assert heroes.modified == datetime( + assert witching.modified == datetime( 2019, 6, 23, 15, 13, 19, - 456634, + 507207, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert heroes.resource_url == "https://metron.cloud/arc/heroes-crisis/" + assert witching.resource_url.__str__() == "https://metron.cloud/arc/witching-hour/" -def test_arcslist(talker: Session) -> None: +def test_arcs_list(talker: Session) -> None: """Test for ArcsList.""" arcs = talker.arcs_list() arc_iter = iter(arcs) assert next(arc_iter).name == "'Til Death Do Us..." + assert next(arc_iter).name == "(She) Drunk History" assert next(arc_iter).name == "1+2 = Fantastic Three" - assert next(arc_iter).name == "1883" - assert len(arcs) == 873 - assert arcs[2].name == "1883" + assert next(arc_iter).name == "1602" + assert len(arcs) == 1418 + assert arcs[3].name == "1602" def test_arc_issue_list(talker: Session) -> None: @@ -66,12 +68,6 @@ def test_bad_arc(talker: Session) -> None: talker.arc(-8) -def test_bad_response_data() -> None: - """Test for bad arc response.""" - with pytest.raises(exceptions.ApiError): - arc.ArcsList({"results": {"name": 1}}) - - def test_bad_arc_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/test_cache.py b/tests/test_cache.py index 3df4164..07ccd00 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -3,6 +3,7 @@ This module contains tests for SqliteCache objects. """ + import pytest import requests_mock diff --git a/tests/test_characters.py b/tests/test_characters.py index 8af16db..909dd1c 100644 --- a/tests/test_characters.py +++ b/tests/test_characters.py @@ -3,22 +3,36 @@ This module contains tests for Character objects. """ + import json from datetime import date, datetime, timedelta, timezone import pytest import requests_mock -from mokkari import character, exceptions +from mokkari import exceptions +from mokkari.schemas.character import Character from mokkari.session import Session +def test_no_alias(talker: Session) -> None: + """Test for no alias attribute.""" + character = talker.character(23843) + assert isinstance(character, Character) + assert character.name == "4-D Man" + assert character.alias is None + assert character.desc == "An alien from the 4th Dimension." + assert character.creators == [] + assert character.teams == [] + assert character.cv_id == 137999 + + def test_known_character(talker: Session) -> None: """Test for a known character.""" - black_bolt: character.CharacterSchema = talker.character(1) + black_bolt = talker.character(1) assert black_bolt.name == "Black Bolt" assert ( - black_bolt.image + black_bolt.image.__str__() == "https://static.metron.cloud/media/character/2018/11/11/black-bolt.jpg" ) assert len(black_bolt.creators) == 2 @@ -33,24 +47,24 @@ def test_known_character(talker: Session) -> None: 90281, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert black_bolt.resource_url == "https://metron.cloud/character/black-bolt/" + assert black_bolt.resource_url.__str__() == "https://metron.cloud/character/black-bolt/" -def test_characterlist(talker: Session) -> None: +def test_character_list(talker: Session) -> None: """Test the CharactersList.""" - character = talker.characters_list({"name": "man"}) - character_iter = iter(character) + chars = talker.characters_list({"name": "man"}) + character_iter = iter(chars) assert next(character_iter).name == "'Mazing Man" assert next(character_iter).name == "3-D Man (Chandler)" assert next(character_iter).name == "3-D Man (Garrett)" - assert len(character) == 576 - assert character[2].name == "3-D Man (Garrett)" + assert len(chars) == 865 + assert chars[2].name == "3-D Man (Garrett)" def test_character_issue_list(talker: Session) -> None: """Test for getting an issue list for an arc.""" issues = talker.character_issues_list(1) - assert len(issues) == 344 + assert len(issues) == 400 assert issues[0].id == 258 assert issues[0].issue_name == "Fantastic Four (1961) #45" assert issues[0].cover_date == date(1965, 12, 1) @@ -67,12 +81,6 @@ def test_bad_character(talker: Session) -> None: talker.character(-1) -def test_bad_response_data() -> None: - """Test for a bad character response.""" - with pytest.raises(exceptions.ApiError): - character.CharactersList({"results": {"name": 1}}) - - def test_bad_character_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/test_creator.py b/tests/test_creator.py index 10d3e69..096c82e 100644 --- a/tests/test_creator.py +++ b/tests/test_creator.py @@ -3,24 +3,25 @@ This module contains tests for Creator objects. """ + import json from datetime import date, datetime, timedelta, timezone import pytest import requests_mock -from mokkari import creator, exceptions +from mokkari import exceptions from mokkari.session import Session def test_known_creator(talker: Session) -> None: """Test for a known creator.""" - jack: creator.CreatorSchema = talker.creator(3) + jack = talker.creator(3) assert jack.name == "Jack Kirby" assert jack.birth == date(1917, 8, 28) assert jack.death == date(1994, 2, 6) assert ( - jack.image + jack.image.__str__() == "https://static.metron.cloud/media/creator/2018/11/11/432124-Jack_Kirby01.jpg" ) assert jack.modified == datetime( @@ -33,23 +34,23 @@ def test_known_creator(talker: Session) -> None: 311024, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert jack.resource_url == "https://metron.cloud/creator/jack-kirby/" + assert jack.resource_url.__str__() == "https://metron.cloud/creator/jack-kirby/" -def test_comiclist(talker: Session) -> None: +def test_comic_list(talker: Session) -> None: """Test the CreatorsList.""" creators = talker.creators_list({"name": "man"}) creator_iter = iter(creators) assert next(creator_iter).name == "A. J. Lieberman" + assert next(creator_iter).name == "Abel Laxamana" assert next(creator_iter).name == "Adam Freeman" assert next(creator_iter).name == "Adam Schlagman" - assert next(creator_iter).name == "Al Sulman" - assert len(creators) == 213 - assert creators[3].name == "Al Sulman" + assert len(creators) == 338 + assert creators[3].name == "Adam Schlagman" def test_bad_creator(talker: Session) -> None: - """Test for a non-existant creator.""" + """Test for a non-existent creator.""" with requests_mock.Mocker() as r: r.get( "https://metron.cloud/api/creator/-1/", @@ -59,12 +60,6 @@ def test_bad_creator(talker: Session) -> None: talker.creator(-1) -def test_bad_response_data() -> None: - """Test for a bad creator response.""" - with pytest.raises(exceptions.ApiError): - creator.CreatorsList({"results": {"name": 1}}) - - def test_bad_creator_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/test_init.py b/tests/test_init.py index 084b995..f4a48e5 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -3,6 +3,7 @@ This module contains tests for project init. """ + import pytest from mokkari import api, exceptions, session diff --git a/tests/test_issues.py b/tests/test_issues.py index 659784b..c2821e7 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -3,6 +3,7 @@ This module contains tests for Issue objects. """ + import json from datetime import date, datetime, timedelta, timezone from decimal import Decimal @@ -10,27 +11,27 @@ import pytest import requests_mock -from mokkari import exceptions, issue +from mokkari import exceptions from mokkari.session import Session def test_issue_with_rating(talker: Session) -> None: """Test issue with a rating.""" - ff: issue.IssueSchema = talker.issue(51658) + ff = talker.issue(51658) assert ff.series.name == "Fantastic Four" assert ff.series.volume == 7 - assert ff.rating.id == 4 - assert ff.rating.name == "Teen Plus" + assert ff.rating.id == 3 + assert ff.rating.name == "Teen" assert ff.cover_date == date(2022, 11, 1) assert ff.store_date == date(2022, 9, 21) assert ff.series.genres[0].id == 10 assert ff.series.genres[0].name == "Super-Hero" - assert ff.resource_url == "https://metron.cloud/issue/fantastic-four-2018-47/" + assert ff.resource_url.__str__() == "https://metron.cloud/issue/fantastic-four-2018-47/" def test_known_issue(talker: Session) -> None: """Test for a known issue.""" - death: issue.IssueSchema = talker.issue(1) + death = talker.issue(1) assert death.publisher.name == "Marvel" assert death.series.name == "Death of the Inhumans" assert death.series.volume == 1 @@ -39,18 +40,20 @@ def test_known_issue(talker: Session) -> None: assert death.store_date == date(2018, 7, 4) assert death.price == Decimal("4.99") assert not death.sku - assert death.image == "https://static.metron.cloud/media/issue/2018/11/11/6497376-01.jpg" + assert ( + death.image.__str__() == "https://static.metron.cloud/media/issue/2018/11/11/6497376-01.jpg" + ) assert len(death.characters) > 0 assert len(death.teams) > 0 assert len(death.credits) > 0 assert death.modified == datetime( - 2022, - 10, - 21, - 9, - 12, + 2023, + 5, 31, - 376522, + 9, + 0, + 46, + 300882, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) assert death.teams[0].name == "Inhumans" @@ -65,12 +68,14 @@ def test_known_issue(talker: Session) -> None: 975156, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert death.resource_url == "https://metron.cloud/issue/death-of-the-inhumans-2018-1/" + assert ( + death.resource_url.__str__() == "https://metron.cloud/issue/death-of-the-inhumans-2018-1/" + ) def test_issue_with_price_and_sku(talker: Session) -> None: """Test issue with price & sku values.""" - die_16: issue.IssueSchema = talker.issue(36860) + die_16 = talker.issue(36860) assert die_16.price == Decimal("3.99") assert die_16.sku == "JUN210207" assert die_16.cover_date == date(2021, 8, 1) @@ -79,7 +84,7 @@ def test_issue_with_price_and_sku(talker: Session) -> None: def test_issue_without_store_date(talker: Session) -> None: """Test issue that does not have a store date.""" - spidey: issue.IssueSchema = talker.issue(31047) + spidey = talker.issue(31047) assert spidey.publisher.name == "Marvel" assert spidey.series.name == "The Spectacular Spider-Man" assert spidey.series.volume == 1 @@ -137,7 +142,7 @@ def test_issue_with_upc_sku_price(talker: Session) -> None: def test_issue_without_upc_sku_price(talker: Session) -> None: """Test issue without upc, sku, and price values.""" - bullets = talker.issue(3980) + bullets = talker.issue(89134) assert bullets.price is None assert bullets.sku == "" assert bullets.upc == "" @@ -150,7 +155,7 @@ def test_issue_with_reprints(talker: Session) -> None: assert wf.number == "228" assert wf.cover_date == date(1975, 3, 1) assert wf.price == Decimal(".6") - assert len(wf.reprints) == 3 + assert len(wf.reprints) == 4 assert wf.reprints[0].id == 35086 assert wf.reprints[0].issue == "Action Comics (1938) #193" assert wf.reprints[1].id == 3645 @@ -166,8 +171,8 @@ def test_issue_with_variants(talker: Session) -> None: assert paprika.series.name == "Mirka Andolfo's Sweet Paprika" assert paprika.series.sort_name == "Mirka Andolfo's Sweet Paprika" assert paprika.series.volume == 1 - assert paprika.series.series_type.name == "Maxi-Series" - assert paprika.series.series_type.id == 4 + assert paprika.series.series_type.name == "Limited Series" + assert paprika.series.series_type.id == 11 assert len(paprika.series.genres) == 1 assert paprika.number == "2" assert paprika.cover_date == date(2021, 9, 1) @@ -180,13 +185,13 @@ def test_issue_with_variants(talker: Session) -> None: assert paprika.variants[0].name == "Cover B Sejic" assert paprika.variants[0].sku == "JUN210257" assert ( - paprika.variants[0].image + paprika.variants[0].image.__str__() == "https://static.metron.cloud/media/variants/2021/08/26/sweet-paprika-2b.jpg" ) assert paprika.variants[1].name == "Cover C March" assert paprika.variants[1].sku == "JUN210258" assert ( - paprika.variants[1].image + paprika.variants[1].image.__str__() == "https://static.metron.cloud/media/variants/2021/08/26/sweet-paprika-2c.jpg" ) @@ -236,12 +241,6 @@ def test_bad_issue(talker: Session) -> None: talker.issue(-1) -def test_bad_response_data() -> None: - """Test for bad issue response.""" - with pytest.raises(exceptions.ApiError): - issue.IssuesList({"results": {"volume": "1"}}) - - def test_multi_page_results(talker: Session) -> None: """Test for multi page results.""" issues = talker.issues_list({"series_name": "action comics", "series_year_began": 1938}) diff --git a/tests/test_publishers.py b/tests/test_publishers.py index 4e853eb..36579e0 100644 --- a/tests/test_publishers.py +++ b/tests/test_publishers.py @@ -3,21 +3,25 @@ This module contains tests for Publisher objects. """ + import json from datetime import datetime, timedelta, timezone import pytest import requests_mock -from mokkari import exceptions, publisher +from mokkari import exceptions from mokkari.session import Session def test_known_publishers(talker: Session) -> None: """Test for a known publisher.""" - marvel: publisher.PublisherSchema = talker.publisher(1) + marvel = talker.publisher(1) assert marvel.name == "Marvel" - assert marvel.image == "https://static.metron.cloud/media/publisher/2018/11/11/marvel.jpg" + assert ( + marvel.image.__str__() + == "https://static.metron.cloud/media/publisher/2018/11/11/marvel.jpg" + ) assert marvel.founded == 1939 assert marvel.modified == datetime( 2019, @@ -29,22 +33,22 @@ def test_known_publishers(talker: Session) -> None: 591390, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert marvel.resource_url == "https://metron.cloud/publisher/marvel/" + assert marvel.resource_url.__str__() == "https://metron.cloud/publisher/marvel/" -def test_publisherlist(talker: Session) -> None: +def test_publisher_list(talker: Session) -> None: """Test the PublishersList.""" publishers = talker.publishers_list() publisher_iter = iter(publishers) assert next(publisher_iter).name == "12-Gauge Comics" + assert next(publisher_iter).name == "AAA Pop Comics" assert next(publisher_iter).name == "AWA Studios" - assert next(publisher_iter).name == "Abrams Books" - assert len(publishers) == 48 - assert publishers[2].name == "Abrams Books" + assert len(publishers) == 93 + assert publishers[2].name == "AWA Studios" def test_bad_publisher(talker: Session) -> None: - """Test for a non-existant publisher.""" + """Test for a non-existent publisher.""" with requests_mock.Mocker() as r: r.get( "https://metron.cloud/api/publisher/-1/", @@ -54,12 +58,6 @@ def test_bad_publisher(talker: Session) -> None: talker.publisher(-1) -def test_bad_response_data() -> None: - """Test for a bad publisher response.""" - with pytest.raises(exceptions.ApiError): - publisher.PublishersList({"results": {"name": 1}}) - - def test_bad_publisher_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/test_role.py b/tests/test_role.py index ec05d6f..f1f0d85 100644 --- a/tests/test_role.py +++ b/tests/test_role.py @@ -3,9 +3,7 @@ This module contains tests for Role objects. """ -import pytest -from mokkari import exceptions, issue from mokkari.session import Session @@ -18,9 +16,3 @@ def test_role_list(talker: Session) -> None: assert next(role_iter).name == "Assistant Editor" assert len(roles) == 11 assert roles[1].name == "Consulting Editor" - - -def test_bad_response_data() -> None: - """Test for a bad role response.""" - with pytest.raises(exceptions.ApiError): - issue.RoleList({"results": {"name": 1}}) diff --git a/tests/test_series.py b/tests/test_series.py index 637ed18..8dae721 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -3,6 +3,7 @@ This module contains tests for Series objects. """ + import json from datetime import datetime, timedelta, timezone @@ -10,34 +11,32 @@ import requests_mock from mokkari import exceptions -from mokkari import series as ser from mokkari.session import Session def test_known_series(talker: Session) -> None: """Test for a known series.""" - death: ser.SeriesSchema = talker.series(1) + death = talker.series(1) assert death.name == "Death of the Inhumans" assert death.sort_name == "Death of the Inhumans" assert death.volume == 1 assert death.year_began == 2018 assert death.year_end == 2018 assert death.issue_count == 5 - assert death.image == "https://static.metron.cloud/media/issue/2018/11/11/6497376-01.jpg" - assert death.series_type.name == "Mini-Series" + assert death.series_type.name == "Limited Series" assert death.publisher.id == 1 assert death.publisher.name == "Marvel" assert death.modified == datetime( - 2019, - 7, - 5, - 14, - 32, - 52, - 239629, + 2023, + 10, + 23, + 16, + 58, + 50, + 526656, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert death.resource_url == "https://metron.cloud/series/death-of-the-inhumans-2018/" + assert death.resource_url.__str__() == "https://metron.cloud/series/death-of-the-inhumans-2018/" def test_series_without_year_end(talker: Session) -> None: @@ -51,24 +50,25 @@ def test_series_without_year_end(talker: Session) -> None: assert abs_carnage.issue_count == 5 assert abs_carnage.publisher.id == 1 assert abs_carnage.publisher.name == "Marvel" - assert abs_carnage.series_type.name == "Mini-Series" + assert abs_carnage.series_type.name == "Limited Series" -def test_serieslist(talker: Session) -> None: +def test_series_list(talker: Session) -> None: """Test the SeriesList.""" series = talker.series_list({"name": "batman"}) series_iter = iter(series) assert next(series_iter).id == 2547 + assert next(series_iter).id == 5959 assert next(series_iter).id == 2481 assert next(series_iter).id == 763 assert next(series_iter).id == 93 - assert len(series) == 162 - assert series[3].id == 93 - assert series[3].display_name == "Batman (2016)" + assert len(series) == 219 + assert series[4].id == 93 + assert series[4].display_name == "Batman (2016)" def test_bad_series(talker: Session) -> None: - """Test for a non-existant series.""" + """Test for a non-existent series.""" with requests_mock.Mocker() as r: r.get( "https://metron.cloud/api/series/-1/", @@ -78,12 +78,6 @@ def test_bad_series(talker: Session) -> None: talker.series(-1) -def test_bad_response_data() -> None: - """Test for a bad series response.""" - with pytest.raises(exceptions.ApiError): - ser.SeriesList({"results": {"name": 1}}) - - def test_bad_series_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/test_series_type.py b/tests/test_series_type.py index 1753d12..7885987 100644 --- a/tests/test_series_type.py +++ b/tests/test_series_type.py @@ -4,10 +4,6 @@ This module contains tests for SeriesType objects. """ -import pytest - -from mokkari import exceptions -from mokkari.series import SeriesTypeList from mokkari.session import Session @@ -18,10 +14,4 @@ def test_series_type_list(talker: Session) -> None: assert next(st_iter).name == "Annual Series" assert next(st_iter).name == "Cancelled Series" assert series_types[3].name == "Hard Cover" - assert len(series_types) == 9 - - -def test_bad_response_data() -> None: - """Test for a bad series type response.""" - with pytest.raises(exceptions.ApiError): - SeriesTypeList({"results": {"name": 1}}) + assert len(series_types) == 8 diff --git a/tests/test_teams.py b/tests/test_teams.py index a63c716..dab5d77 100644 --- a/tests/test_teams.py +++ b/tests/test_teams.py @@ -3,21 +3,24 @@ This module contains tests for Team objects. """ + import json from datetime import date, datetime, timedelta, timezone import pytest import requests_mock -from mokkari import exceptions, team +from mokkari import exceptions from mokkari.session import Session def test_known_team(talker: Session) -> None: """Test for a known team.""" - inhumans: team.TeamSchema = talker.team(1) + inhumans = talker.team(1) assert inhumans.name == "Inhumans" - assert inhumans.image == "https://static.metron.cloud/media/team/2018/11/11/Inhumans.jpg" + assert ( + inhumans.image.__str__() == "https://static.metron.cloud/media/team/2018/11/11/Inhumans.jpg" + ) assert len(inhumans.creators) == 2 assert inhumans.modified == datetime( 2019, @@ -29,31 +32,32 @@ def test_known_team(talker: Session) -> None: 975156, tzinfo=timezone(timedelta(days=-1, seconds=72000), "-0400"), ) - assert inhumans.resource_url == "https://metron.cloud/team/inhumans/" + assert inhumans.resource_url.__str__() == "https://metron.cloud/team/inhumans/" -def test_teamlist(talker: Session) -> None: +def test_team_list(talker: Session) -> None: """Test the TeamsList.""" teams = talker.teams_list() team_iter = iter(teams) + assert next(team_iter).name == "501st Legion" assert next(team_iter).name == "A-Force" assert next(team_iter).name == "A-Next" assert next(team_iter).name == "A.I.M." - assert len(teams) == 591 - assert teams[2].name == "A.I.M." + assert len(teams) == 1331 + assert teams[3].name == "A.I.M." def test_team_issue_list(talker: Session) -> None: """Test for getting an issue list for an arc.""" issues = talker.team_issues_list(1) - assert len(issues) == 515 + assert len(issues) == 609 assert issues[0].id == 258 assert issues[0].issue_name == "Fantastic Four (1961) #45" assert issues[0].cover_date == date(1965, 12, 1) def test_bad_team(talker: Session) -> None: - """Test for a non-existant team.""" + """Test for a non-existent team.""" with requests_mock.Mocker() as r: r.get( "https://metron.cloud/api/team/-1/", @@ -63,12 +67,6 @@ def test_bad_team(talker: Session) -> None: talker.team(-1) -def test_bad_response_data() -> None: - """Test for a bad team response.""" - with pytest.raises(exceptions.ApiError): - team.TeamsList({"results": {"name": 1}}) - - def test_bad_team_validate(talker: Session) -> None: """Test data with invalid data.""" # Change the 'name' field to an int, when it should be a string. diff --git a/tests/testing_mock.sqlite b/tests/testing_mock.sqlite index 78c9e0b..1dc87fa 100644 Binary files a/tests/testing_mock.sqlite and b/tests/testing_mock.sqlite differ diff --git a/tox.ini b/tox.ini index e5bd9fe..1f72d8c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] isolated_build = true -envlist = py39, py310, py311 +envlist = py310, py311, py312 [testenv] allowlist_externals = poetry