panther 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
panther/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .main import Panther
2
2
 
3
- __version__ = '1.4.0'
3
+ __version__ = '1.5.0'
4
4
 
5
5
 
6
6
  def version():
panther/db/connection.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from redis import Redis
2
2
  from pantherdb import PantherDB
3
3
  from panther.configs import config
4
+ from panther.cli.utils import error
4
5
 
5
6
 
6
7
  class Singleton(object):
@@ -19,9 +20,8 @@ class DBSession(Singleton):
19
20
  if db_url:
20
21
  self._db_name = db_url[:db_url.find(':')]
21
22
  match self._db_name:
22
- # case 'mongodb':
23
- # # TODO: Check pymongo installed or not
24
- # self._create_mongodb_session(db_url)
23
+ case 'mongodb':
24
+ self._create_mongodb_session(db_url)
25
25
  case 'pantherdb':
26
26
  self._create_pantherdb_session(db_url[12:])
27
27
  case _:
@@ -37,8 +37,12 @@ class DBSession(Singleton):
37
37
  return self._db_name
38
38
 
39
39
  def _create_mongodb_session(self, db_url: str) -> None:
40
- from pymongo import MongoClient
41
- from pymongo.database import Database
40
+ try:
41
+ from pymongo import MongoClient
42
+ from pymongo.database import Database
43
+ except ImportError:
44
+ error('No module named "pymongo"\n\nHint: Try to install with "pip install pymongo"')
45
+ raise ImportError(error)
42
46
  self._client: MongoClient = MongoClient(db_url)
43
47
  self._session: Database = self._client.get_database()
44
48
 
@@ -56,7 +60,6 @@ class RedisConnection(Singleton, Redis):
56
60
  is_connected: bool = False
57
61
 
58
62
  def __init__(self, host: str | None = None, port: int | None = None, **kwargs):
59
- # TODO: Check redis installed or not
60
63
  if host and port:
61
64
  super().__init__(host=host, port=port, **kwargs)
62
65
  self.is_connected = True
@@ -1,90 +1,85 @@
1
- from typing import TypeVar
2
-
3
- import bson
1
+ from typing import Self
4
2
 
5
3
  from panther.db.connection import db # NOQA: F401
6
- from panther.db.utils import clean_object_id_in_dicts, merge_dicts, log_query
7
-
8
- # TODO: Not sure about this bounding
9
- T = TypeVar('T', bound='BaseMongoDBQuery')
4
+ from panther.exceptions import DBException
5
+ from panther.db.utils import clean_object_id_in_dicts, merge_dicts
10
6
 
11
7
 
12
8
  class BaseMongoDBQuery:
13
9
 
14
10
  @classmethod
15
- def get_one(cls: type[T], _data: dict = None, /, **kwargs) -> T:
16
- clean_object_id_in_dicts(_data, kwargs)
17
- _query = merge_dicts(_data, kwargs)
18
- obj = eval(f'db.session.{cls.__name__}.find_one(_query)')
19
- return cls(**obj) if obj else None
11
+ def _merge(cls, *args) -> dict:
12
+ clean_object_id_in_dicts(*args)
13
+ # TODO: Convert "id" to "_id"
14
+ return merge_dicts(*args)
20
15
 
16
+ # # # # # Find # # # # #
21
17
  @classmethod
22
- def count(cls, _data: dict = None, /, **kwargs) -> int:
23
- clean_object_id_in_dicts(_data, kwargs)
24
- _query = merge_dicts(_data, kwargs)
25
- return eval(f'db.session.{cls.__name__}.count_documents(_query)')
18
+ def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
19
+ _query = cls._merge(_data, kwargs)
20
+ if document := eval(f'db.session.{cls.__name__}.find_one(_query)'):
21
+ return cls(**document)
26
22
 
27
23
  @classmethod
28
- def list(cls, _data: dict = None, /, **kwargs):
29
- clean_object_id_in_dicts(_data, kwargs)
30
- _query = merge_dicts(_data, kwargs)
31
- result = eval(f'db.session.{cls.__name__}.find(_query)')
32
- return [cls(**obj) for obj in result]
24
+ def find(cls, _data: dict = None, /, **kwargs) -> list[Self]:
25
+ _query = cls._merge(_data, kwargs)
26
+ documents = eval(f'db.session.{cls.__name__}.find(_query)')
27
+ return [cls(**document) for document in documents]
33
28
 
29
+ # # # # # Insert # # # # #
34
30
  @classmethod
35
- def create(cls, _data: dict = None, **kwargs) -> bson.objectid.ObjectId:
36
- _query = merge_dicts(_data, kwargs)
37
- return eval(f'db.session.{cls.__name__}.insert_one(_query)').inserted_id
31
+ def insert_one(cls, _data: dict = None, **kwargs) -> Self:
32
+ document = cls._merge(_data, kwargs)
33
+ document['id'] = eval(f'db.session.{cls.__name__}.insert_one(document)').inserted_id
34
+ return cls(**document)
38
35
 
39
- def delete(self) -> bool:
36
+ @classmethod
37
+ def insert_many(cls, _data: dict = None, **kwargs) -> Self:
38
+ raise DBException('insert_many() is not supported while using MongoDB.')
39
+
40
+ # # # # # Delete # # # # #
41
+ def delete(self) -> None:
40
42
  _filter = {'_id': self._id}
41
- result = eval(f'db.session.{self.__class__.__name__}.delete_one(_filter)')
42
- return bool(result.deleted_count)
43
+ eval(f'db.session.{self.__class__.__name__}.delete_one(_filter)')
43
44
 
44
45
  @classmethod
45
- def delete_one(cls, **kwargs) -> bool:
46
- clean_object_id_in_dicts(kwargs)
47
- result = eval(f'db.session.{cls.__name__}.delete_one(kwargs)')
46
+ def delete_one(cls, _data: dict = None, /, **kwargs) -> bool:
47
+ _query = cls._merge(_data, kwargs)
48
+ result = eval(f'db.session.{cls.__name__}.delete_one(_query)')
48
49
  return bool(result.deleted_count)
49
50
 
50
51
  @classmethod
51
- def delete_many(cls, **kwargs) -> int:
52
- clean_object_id_in_dicts(kwargs)
53
- result = eval(f'db.session.{cls.__name__}.delete_many(kwargs)')
52
+ def delete_many(cls, _data: dict = None, /, **kwargs) -> int:
53
+ _query = cls._merge(_data, kwargs)
54
+ result = eval(f'db.session.{cls.__name__}.delete_many(_query)')
54
55
  return result.deleted_count
55
56
 
56
- def update(self, _data: dict = None, **kwargs) -> dict:
57
- for field, value in (_data or kwargs).items():
58
- if hasattr(self, field):
59
- setattr(self, field, value)
57
+ # # # # # Update # # # # #
58
+ def update(self, **kwargs) -> None:
59
+ for field, value in kwargs.items():
60
+ setattr(self, field, value)
60
61
  _filter = {'_id': self._id}
61
62
  _update = {'$set': kwargs}
62
- return eval(f'db.session.{self.__class__.__name__}.update_one(_filter, _update)')
63
+ eval(f'db.session.{self.__class__.__name__}.update_one(_filter, _update)')
63
64
 
64
65
  @classmethod
65
- def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> dict:
66
+ def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> bool:
66
67
  clean_object_id_in_dicts(_filter)
67
-
68
68
  _update = {'$set': kwargs | {}}
69
69
  if isinstance(_data, dict):
70
70
  _data['$set'] = _data.get('$set', {}) | (kwargs or {})
71
71
 
72
- return eval(f'db.session.{cls.__name__}.update_one(_filter, _data | _update)')
73
-
74
- @classmethod
75
- def update_many(cls, _filter, **kwargs) -> dict:
76
- _update = {'$set': kwargs}
77
- return eval(f'db.session.{cls.__name__}.update_many(_filter, _update)')
72
+ result = eval(f'db.session.{cls.__name__}.update_one(_filter, _data | _update)')
73
+ return bool(result.updated_count)
78
74
 
79
75
  @classmethod
80
- def increment(cls, _filter, **kwargs):
81
- _update = {'$inc': kwargs}
82
- return eval(f'db.session.{cls.__name__}.update_many({_filter}, {_update})')
76
+ def update_many(cls, _filter, _data: dict = None, /, **kwargs) -> int:
77
+ _update = {'$set': cls._merge(_data, kwargs)}
78
+ result = eval(f'db.session.{cls.__name__}.update_many(_filter, _update)')
79
+ return result.updated_count
83
80
 
81
+ # # # # # Other # # # # #
84
82
  @classmethod
85
- def get_or_create(cls, **kwargs) -> tuple[bool, any]:
86
- obj = cls.get_one(**kwargs)
87
- if obj:
88
- return False, obj
89
- else:
90
- return True, cls.create(**kwargs)
83
+ def count(cls, _data: dict = None, /, **kwargs) -> int:
84
+ _query = cls._merge(_data, kwargs)
85
+ return eval(f'db.session.{cls.__name__}.count_documents(_query)')
@@ -1,5 +1,4 @@
1
1
  from typing import Self
2
- from pydantic import ValidationError
3
2
 
4
3
  from panther.db.connection import db
5
4
  from panther.db.utils import merge_dicts
@@ -8,26 +7,6 @@ from panther.exceptions import DBException
8
7
 
9
8
  class BasePantherDBQuery:
10
9
 
11
- @classmethod
12
- def validate_data(cls, data: dict, is_updating: bool = False) -> Self | None:
13
- """
14
- *. Validate the input of user with its class
15
- *. If is_updating is True & exception happens but the message was empty
16
- Then return nothing (we don't need the obj on update())
17
- & we don't have access to the obj after exception ...
18
- """
19
- try:
20
- obj = cls(**data)
21
- except ValidationError as validation_error:
22
- error = ', '.join(
23
- '{field}="{error}"'.format(field=e['loc'][0], error=e['msg'])
24
- for e in validation_error.errors() if not is_updating or e['type'] != 'value_error.missing')
25
- if error:
26
- message = f'{cls.__name__}({error})'
27
- raise DBException(message)
28
- else:
29
- return obj
30
-
31
10
  @classmethod
32
11
  def _merge(cls, *args) -> dict:
33
12
  # TODO: Convert "id" to "_id"
@@ -36,7 +15,7 @@ class BasePantherDBQuery:
36
15
  # # # # # Find # # # # #
37
16
  @classmethod
38
17
  def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
39
- if document := db.session.collection(cls.__name__).first(**cls._merge(_data, kwargs)):
18
+ if document := db.session.collection(cls.__name__).find_one(**cls._merge(_data, kwargs)):
40
19
  return cls(**document)
41
20
 
42
21
  @classmethod
@@ -47,13 +26,12 @@ class BasePantherDBQuery:
47
26
  # # # # # Insert # # # # #
48
27
  @classmethod
49
28
  def insert_one(cls, _data: dict = None, **kwargs) -> Self:
50
- obj = cls.validate_data(kwargs)
51
- obj.id = db.session.collection(cls.__name__).insert_one(**cls._merge(_data, kwargs))['_id']
52
- return obj
29
+ document = db.session.collection(cls.__name__).insert_one(**cls._merge(_data, kwargs))
30
+ return cls(**document)
53
31
 
54
32
  @classmethod
55
33
  def insert_many(cls, _data: dict = None, /, **kwargs):
56
- raise DBException('increment() is not supported while using SlarkDB.')
34
+ raise DBException('insert_many() is not supported while using PantherDB.')
57
35
 
58
36
  # # # # # Delete # # # # #
59
37
  def delete(self) -> None:
@@ -69,7 +47,8 @@ class BasePantherDBQuery:
69
47
 
70
48
  # # # # # Update # # # # #
71
49
  def update(self, **kwargs) -> None:
72
- self.validate_data(kwargs, is_updating=True)
50
+ for field, value in kwargs.items():
51
+ setattr(self, field, value)
73
52
  db.session.collection(self.__class__.__name__).update_one({'_id': self.id}, **kwargs)
74
53
 
75
54
  @classmethod
@@ -81,16 +60,6 @@ class BasePantherDBQuery:
81
60
  return db.session.collection(cls.__name__).update_many(_filter, **cls._merge(_data, kwargs))
82
61
 
83
62
  # # # # # Other # # # # #
84
- @classmethod
85
- def first(cls, _data: dict = None, /, **kwargs) -> Self | None:
86
- if document := db.session.collection(cls.__name__).first(**cls._merge(_data, kwargs)):
87
- return cls(**document)
88
-
89
- @classmethod
90
- def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
91
- if document := db.session.collection(cls.__name__).last(**cls._merge(_data, kwargs)):
92
- return cls(**document)
93
-
94
63
  @classmethod
95
64
  def count(cls, _data: dict = None, /, **kwargs) -> int:
96
65
  return db.session.collection(cls.__name__).count(**cls._merge(_data, kwargs))
@@ -1,9 +1,12 @@
1
1
  from typing import Self
2
2
 
3
+ from pydantic import ValidationError
4
+
3
5
  from panther.configs import config
4
6
  from panther.db.utils import log_query
5
7
  from panther.db.queries.mongodb_queries import BaseMongoDBQuery
6
8
  from panther.db.queries.pantherdb_queries import BasePantherDBQuery
9
+ from panther.exceptions import DBException
7
10
 
8
11
  if config['db_engine'] == 'pantherdb':
9
12
  BaseQuery = BasePantherDBQuery
@@ -17,6 +20,22 @@ __all__ = (
17
20
 
18
21
  class Query(BaseQuery):
19
22
 
23
+ @classmethod
24
+ def validate_data(cls, data: dict, is_updating: bool = False) -> Self | None:
25
+ """
26
+ *. Validate the input of user with its class
27
+ *. If is_updating is True & exception happens but the message was empty
28
+ """
29
+ try:
30
+ cls(**data)
31
+ except ValidationError as validation_error:
32
+ error = ', '.join(
33
+ '{field}="{error}"'.format(field=e['loc'][0], error=e['msg'])
34
+ for e in validation_error.errors() if not is_updating or e['type'] != 'value_error.missing')
35
+ if error:
36
+ message = f'{cls.__name__}({error})'
37
+ raise DBException(message)
38
+
20
39
  # # # # # Find # # # # #
21
40
  @classmethod
22
41
  @log_query
@@ -47,6 +66,7 @@ class Query(BaseQuery):
47
66
  >>> from example.app.models import User
48
67
  >>> User.insert_one(name='Ali', age=24, ...)
49
68
  """
69
+ cls.validate_data(kwargs)
50
70
  return super().insert_one(_data, **kwargs)
51
71
 
52
72
  @classmethod
@@ -94,6 +114,7 @@ class Query(BaseQuery):
94
114
  >>> user = User.find_one(name='Ali')
95
115
  >>> user.update(name='Saba')
96
116
  """
117
+ self.validate_data(kwargs, is_updating=True)
97
118
  return super().update(**kwargs)
98
119
 
99
120
  @classmethod
@@ -120,31 +141,33 @@ class Query(BaseQuery):
120
141
  # # # # # Other # # # # #
121
142
  @classmethod
122
143
  @log_query
123
- def first(cls, _data: dict = None, /, **kwargs) -> Self | None:
144
+ def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
124
145
  """
125
- It works same as find_one()
126
146
  example:
127
147
  >>> from example.app.models import User
128
- >>> User.first(name='Ali')
148
+ >>> user = User.last(name='Ali')
129
149
  """
130
- return super().first(_data, **kwargs)
150
+ return super().last(_data, **kwargs)
131
151
 
132
152
  @classmethod
133
153
  @log_query
134
- def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
154
+ def count(cls, _data: dict = None, /, **kwargs) -> int:
135
155
  """
136
156
  example:
137
157
  >>> from example.app.models import User
138
- >>> User.last(name='Ali')
158
+ >>> User.count(name='Ali')
139
159
  """
140
- return super().last(_data, **kwargs)
160
+ return super().count(_data, **kwargs)
141
161
 
142
162
  @classmethod
143
163
  @log_query
144
- def count(cls, _data: dict = None, /, **kwargs) -> int:
164
+ def find_or_insert(cls, **kwargs) -> tuple[bool, any]:
145
165
  """
146
166
  example:
147
167
  >>> from example.app.models import User
148
- >>> User.count(name='Ali')
168
+ >>> user = User.find_or_insert(name='Ali')
149
169
  """
150
- return super().count(_data, **kwargs)
170
+ if obj := cls.find_one(**kwargs):
171
+ return False, obj
172
+ else:
173
+ return True, cls.insert_one(**kwargs)
panther/main.py CHANGED
@@ -4,12 +4,12 @@ from runpy import run_path
4
4
  from pydantic.main import ModelMetaclass
5
5
 
6
6
  from panther import status
7
- from panther.configs import JWTConfig, config
7
+ from panther.request import Request
8
+ from panther.response import Response
8
9
  from panther.exceptions import APIException
10
+ from panther.configs import JWTConfig, config
9
11
  from panther.middlewares.base import BaseMiddleware
10
12
  from panther.middlewares.monitoring import Middleware as MonitoringMiddleware
11
- from panther.request import Request
12
- from panther.response import Response
13
13
  from panther.routings import find_endpoint, check_urls, collect_urls, finalize_urls
14
14
  from panther._utils import http_response, import_class, read_body, collect_path_variables
15
15
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: Fast & Friendly, Web Framework For Building Async APIs
5
5
  Home-page: https://github.com/alirn76/panther
6
6
  Author: Ali RajabNezhad
@@ -14,7 +14,8 @@ Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: pantherdb (>=1.2.0)
16
16
  Requires-Dist: pydantic (>=1.10.5)
17
- Requires-Dist: uvicorn (==0.20.0)
17
+ Requires-Dist: watchfiles (>=0.18.1)
18
+ Requires-Dist: uvicorn[standard]
18
19
  Requires-Dist: bpython (>=0.24)
19
20
  Requires-Dist: bson (>=0.5.10)
20
21
  Requires-Dist: redis (>=4.0.2)
@@ -34,7 +35,7 @@ Requires-Dist: pymongo (>=4.3.3) ; extra == 'full'
34
35
  ---
35
36
 
36
37
  ### Features
37
- - Document-oriented Databases ORM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
38
+ - Document-oriented Databases ODM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
38
39
  - Visual API Monitoring (In Terminal)
39
40
  - Cache APIs (In Memory, In Redis)
40
41
  - Built-in Authentication Classes (Customizable)
@@ -1,4 +1,4 @@
1
- panther/__init__.py,sha256=ViOJcmBWhbt9e0b1kOj-Y3RfqE4-sRmE9jJ0CwqE-FM,89
1
+ panther/__init__.py,sha256=qhYoTW71O9z3-iK8zrAILsgYWzJ_tcpZQaqQoqWIbdw,89
2
2
  panther/_utils.py,sha256=Ltev6PCrTWHTWSQcWNfHMWDof1_MXODTAcDaGwflgXo,3667
3
3
  panther/app.py,sha256=tojfQaU8x5pZa7m8XS4m9fR7D5sIe_-d96hD2ccBINo,5469
4
4
  panther/authentications.py,sha256=FCRQL3xqN1Tla2vZsOY8-qXwPhnZBFsujJ-R7CeaW7w,2350
@@ -6,7 +6,7 @@ panther/caching.py,sha256=qCqb-IaXD1T7VU0AzJVMoXawb366ZYob9kblDUT-7sM,2452
6
6
  panther/configs.py,sha256=bRGRy772Rxjb3RunEBBvfa9LCNCmgcc2TKkfbNTK0GM,975
7
7
  panther/exceptions.py,sha256=c1MeSo7_uwDuqoBcjKTFyXz17uXP5iZ3bM0Sp5Stvhw,769
8
8
  panther/logger.py,sha256=1skaZg4w55mTh4pSrCohkGp-7dg62lyEIoFAJKMP5L8,2499
9
- panther/main.py,sha256=Aa43X3ZEonPrJ4Mj5ACEyGmJl-BbsHcPyKj3fDe2E4k,7460
9
+ panther/main.py,sha256=n9Q55NOrx0KgumQOi8Ra3CzxtYPGWZHMKkX4g44e9LQ,7460
10
10
  panther/request.py,sha256=Ix1podGHCU5NlUCT9Emc79GOITKEw623ldpTw3oZm68,4383
11
11
  panther/response.py,sha256=zzfe8vsPSnydN-_peJTizlnmnawxlNbJFU9zvXppYTk,1025
12
12
  panther/routings.py,sha256=sV3xWksKYb3_95BTDOtSqouroHkB98ExoNkgWT1t7jE,4422
@@ -20,21 +20,21 @@ panther/cli/run_command.py,sha256=SdguosH7Orlh-8x8eZK_EWrv8MPnXFKzqxruTHYdHqk,15
20
20
  panther/cli/template.py,sha256=KhehW5nzovQzbW75J1lpAAQcIsqucqU4IXQXpVWBj5M,2317
21
21
  panther/cli/utils.py,sha256=QKQ5_qLWtTjpLY0lZ6yt2vDlc2H0jF6HFe3F-8YNJDg,8225
22
22
  panther/db/__init__.py,sha256=lOgqqRkGMzMSS1WS8D7lTChTr9ikIB5ImRPmucOhCyc,54
23
- panther/db/connection.py,sha256=BoqTetHcWn2cmjo1ELDoFFGkGlvJw6qDuBorKSe8ru0,2097
23
+ panther/db/connection.py,sha256=E6ZRkDY1b-ns6cYBkSCl-DTYLzbRuOaLYFZvU0fauKs,2207
24
24
  panther/db/models.py,sha256=vnhFtVONqhCkTkFMOSz_rTDmAAxNcegPoxk1p5SljLc,1028
25
25
  panther/db/utils.py,sha256=6ndrZ9reaVTqnbFhX8MjOYtcrcPSY7UywAbDquY4eEg,1219
26
26
  panther/db/queries/__init__.py,sha256=BMffHS9RbHE-AUAeT9C5uY3L-hpDh0WGRduDUQ9Kpuc,41
27
- panther/db/queries/mongodb_queries.py,sha256=qFW1tsxnyh459rFOOD-jPTZwhDFh6q6qSttA_a4ryaw,3228
28
- panther/db/queries/pantherdb_queries.py,sha256=CEKxBcFZo4PmcleE87Jw1PrujtOB-emd20mqzHI1IKw,3834
29
- panther/db/queries/queries.py,sha256=yG5B_qg4cNL8nDkeJtJ0RRj4EH5dEMC-2jNIk4I1eS0,4134
27
+ panther/db/queries/mongodb_queries.py,sha256=uXd81Q2ukJskFDYlNfOiuCCkHuH7URyfZMkD6MUiVkk,3205
28
+ panther/db/queries/pantherdb_queries.py,sha256=Ee3Kb5WIGQ-ZDeAYFHYx_Y34f-I0RgLT0XYFEof5FLM,2505
29
+ panther/db/queries/queries.py,sha256=MD65JzBtot-2fKbePVNBF5wymsywXfTm3mJwAE2dkIk,5052
30
30
  panther/middlewares/__init__.py,sha256=7RtHuS-MfybnJc6pcBSGhi9teXNhDsnJ3n7h_cXSkJk,66
31
31
  panther/middlewares/base.py,sha256=Php29ckITeGZm6GfauFG3i61bcsb4qoU8RpPLTqsfls,240
32
32
  panther/middlewares/db.py,sha256=C_PevTIaMykJl0NaaMYEfwE_oLdSLfKW2HR9UoPN1dU,508
33
33
  panther/middlewares/monitoring.py,sha256=CbGNJ4Ja-m9Wq3Ytv7RFyd-Qj9Ft6cYqBZZncCAxccs,852
34
34
  panther/middlewares/redis.py,sha256=_XIvPAOrVl74UoiA6-Xz8hSfWoeuM2Y1e74xeyvW0I4,1126
35
- panther-1.4.0.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
36
- panther-1.4.0.dist-info/METADATA,sha256=AnzClL3p1FGVv2WWYBoSfJdMrIG3If4tq43T4zXx_Sk,5124
37
- panther-1.4.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
38
- panther-1.4.0.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
39
- panther-1.4.0.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
40
- panther-1.4.0.dist-info/RECORD,,
35
+ panther-1.5.0.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
36
+ panther-1.5.0.dist-info/METADATA,sha256=N_WVMfNbFUZfevcMvOxEANoqN03SXZWeBlb65BLgahc,5160
37
+ panther-1.5.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
38
+ panther-1.5.0.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
39
+ panther-1.5.0.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
40
+ panther-1.5.0.dist-info/RECORD,,