PyFunceble-dev 4.2.7__py3-none-any.whl → 4.2.10__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.
Files changed (51) hide show
  1. PyFunceble/checker/availability/base.py +8 -8
  2. PyFunceble/checker/availability/domain.py +0 -1
  3. PyFunceble/checker/availability/extras/base.py +1 -2
  4. PyFunceble/checker/availability/extras/rules.py +1 -1
  5. PyFunceble/checker/availability/ip.py +0 -1
  6. PyFunceble/checker/availability/url.py +0 -1
  7. PyFunceble/checker/reputation/base.py +0 -1
  8. PyFunceble/checker/reputation/domain.py +0 -1
  9. PyFunceble/checker/reputation/ip.py +0 -1
  10. PyFunceble/checker/reputation/url.py +0 -1
  11. PyFunceble/checker/syntax/second_lvl_domain.py +6 -2
  12. PyFunceble/checker/syntax/status.py +1 -0
  13. PyFunceble/checker/syntax/subdomain.py +3 -1
  14. PyFunceble/checker/syntax/url.py +0 -1
  15. PyFunceble/cli/credential_loader.py +3 -3
  16. PyFunceble/cli/entry_points/pyfunceble/cli.py +72 -6
  17. PyFunceble/cli/file_preloader.py +3 -3
  18. PyFunceble/cli/migrators/base.py +0 -1
  19. PyFunceble/cli/processes/workers/base.py +15 -6
  20. PyFunceble/cli/processes/workers/producer.py +5 -0
  21. PyFunceble/cli/scripts/production.py +3 -4
  22. PyFunceble/cli/storage.py +5 -3
  23. PyFunceble/cli/system/launcher.py +47 -1
  24. PyFunceble/cli/utils/testing.py +1 -3
  25. PyFunceble/config/loader.py +10 -0
  26. PyFunceble/data/alembic/mysql/versions/35c79626ecb9_fix_some_columns.py +1 -0
  27. PyFunceble/data/alembic/mysql/versions/3a4c55a9320d_add_continue_table.py +1 -0
  28. PyFunceble/data/alembic/mysql/versions/3d6f4a33cdb2_add_inactive_table.py +1 -0
  29. PyFunceble/data/alembic/mysql/versions/45713fea8097_deletion_uneeded_columns_from_whois_.py +1 -0
  30. PyFunceble/data/alembic/mysql/versions/459a0d7b8f09_add_idna_subject_column_into_whois.py +1 -0
  31. PyFunceble/data/alembic/mysql/versions/6f4729deaf03_delete_inactive_source_column.py +1 -0
  32. PyFunceble/data/alembic/mysql/versions/7bcf7fa64ba1_rename_created_to_created_at_and.py +1 -0
  33. PyFunceble/data/alembic/mysql/versions/83ada95132bf_delete_the_file_table.py +1 -0
  34. PyFunceble/data/alembic/mysql/versions/912bbcb77a6c_add_registrar_column.py +1 -0
  35. PyFunceble/data/alembic/mysql/versions/95dc17ddd729_introduction_of_the_session_id_column.py +1 -0
  36. PyFunceble/data/alembic/mysql/versions/ade87195b0a0_base.py +1 -0
  37. PyFunceble/data/alembic/mysql/versions/bef7bcaac3f2_make_id_a_bigint.py +1 -0
  38. PyFunceble/data/alembic/mysql/versions/d8893cd406db_allow_whois_record_to_be_empty_null.py +1 -0
  39. PyFunceble/data/alembic/mysql/versions/e04e8301d1a2_deletion_of_the_mined_table.py +1 -0
  40. PyFunceble/data/alembic/postgresql/versions/a32ac5d66eee_initial_version.py +1 -0
  41. PyFunceble/data/infrastructure/.PyFunceble_production.yaml +3 -0
  42. PyFunceble/query/collection.py +207 -11
  43. PyFunceble/query/requests/adapter/https.py +6 -6
  44. PyFunceble/query/whois/converter/digit2digits.py +1 -0
  45. PyFunceble/storage.py +4 -2
  46. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/METADATA +42 -42
  47. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/RECORD +51 -51
  48. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/WHEEL +1 -1
  49. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/LICENSE +0 -0
  50. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/entry_points.txt +0 -0
  51. {PyFunceble_dev-4.2.7.dist-info → PyFunceble_dev-4.2.10.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ Revises: 35c79626ecb9
5
5
  Create Date: 2020-08-22 17:27:52.087506
6
6
 
7
7
  """
8
+
8
9
  from alembic import op
9
10
  from sqlalchemy.dialects import mysql
10
11
 
@@ -5,6 +5,7 @@ Revises: d8893cd406db
5
5
  Create Date: 2020-12-07 12:33:43.650514
6
6
 
7
7
  """
8
+
8
9
  import sqlalchemy as sa
9
10
  from alembic import op
10
11
  from sqlalchemy.dialects import mysql
@@ -5,6 +5,7 @@ Revises:
5
5
  Create Date: 2022-12-28 07:20:50.310072
6
6
 
7
7
  """
8
+
8
9
  import sqlalchemy as sa
9
10
  from alembic import op
10
11
 
@@ -173,6 +173,9 @@ cli_testing:
173
173
  # Activates the reputation test.
174
174
  reputation: no
175
175
 
176
+ # BETA: Activates the platform contribution test.
177
+ platform_contribution: no
178
+
176
179
  days_between:
177
180
  # Provides everything which is x days periodic.
178
181
 
@@ -51,7 +51,7 @@ License:
51
51
  """
52
52
 
53
53
  import json
54
- from typing import List, Optional, Union
54
+ from typing import Generator, List, Optional, Union
55
55
 
56
56
  import requests
57
57
  import requests.exceptions
@@ -88,6 +88,7 @@ class CollectionQueryTool:
88
88
 
89
89
  STD_URL_BASE: str = "http://localhost:8001"
90
90
  STD_PREFERRED_STATUS_ORIGIN: str = "frequent"
91
+ STD_TIMEOUT: float = 5.0
91
92
 
92
93
  _token: Optional[str] = None
93
94
  """
@@ -109,6 +110,11 @@ class CollectionQueryTool:
109
110
  Whether we are working with the modern or legacy API.
110
111
  """
111
112
 
113
+ _timeout: float = 5.0
114
+ """
115
+ The timeout to use while communicating with the API.
116
+ """
117
+
112
118
  session: Optional[requests.Session] = None
113
119
 
114
120
  def __init__(
@@ -117,6 +123,7 @@ class CollectionQueryTool:
117
123
  token: Optional[str] = None,
118
124
  url_base: Optional[str] = None,
119
125
  preferred_status_origin: Optional[str] = None,
126
+ timeout: Optional[float] = None,
120
127
  ) -> None:
121
128
  if token is not None:
122
129
  self.token = token
@@ -135,6 +142,11 @@ class CollectionQueryTool:
135
142
  else:
136
143
  self.guess_and_set_preferred_status_origin()
137
144
 
145
+ if timeout is not None:
146
+ self.timeout = timeout
147
+ else:
148
+ self.guess_and_set_timeout()
149
+
138
150
  self.session = requests.Session()
139
151
  self.session.headers.update(
140
152
  {
@@ -283,6 +295,43 @@ class CollectionQueryTool:
283
295
 
284
296
  return self
285
297
 
298
+ @property
299
+ def timeout(self) -> float:
300
+ """
301
+ Provides the value of the :code:`_timeout` attribute.
302
+ """
303
+
304
+ return self._timeout
305
+
306
+ @timeout.setter
307
+ def timeout(self, value: float) -> None:
308
+ """
309
+ Sets the value of the :code:`_timeout` attribute.
310
+
311
+ :param value:
312
+ The value to set.
313
+
314
+ :raise TypeError:
315
+ When the given :code:`value` is not a :py:class:`float`.
316
+ """
317
+
318
+ if not isinstance(value, (int, float)):
319
+ raise TypeError(f"<value> should be {float}, {type(value)} given.")
320
+
321
+ self._timeout = value
322
+
323
+ def set_timeout(self, value: float) -> "CollectionQueryTool":
324
+ """
325
+ Sets the value of the :code:`_timeout` attribute.
326
+
327
+ :param value:
328
+ The value to set.
329
+ """
330
+
331
+ self.timeout = value
332
+
333
+ return self
334
+
286
335
  def guess_and_set_url_base(self) -> "CollectionQueryTool":
287
336
  """
288
337
  Try to guess the URL base to work with.
@@ -307,16 +356,22 @@ class CollectionQueryTool:
307
356
  Try to guess if we are working with a legacy version.
308
357
  """
309
358
 
310
- try:
311
- response = self.session.get(
312
- f"{self.url_base}/v1/stats/subject",
313
- )
359
+ if self.token:
360
+ try:
361
+ response = self.session.get(
362
+ f"{self.url_base}/v1/stats/subject",
363
+ timeout=self.timeout,
364
+ )
314
365
 
315
- response.raise_for_status()
366
+ response.raise_for_status()
316
367
 
368
+ self.is_modern_api = False
369
+ except (requests.RequestException, json.decoder.JSONDecodeError):
370
+ self.is_modern_api = True
371
+ else:
317
372
  self.is_modern_api = False
318
- except (requests.RequestException, json.decoder.JSONDecodeError):
319
- self.is_modern_api = True
373
+
374
+ return self
320
375
 
321
376
  @property
322
377
  def preferred_status_origin(self) -> Optional[str]:
@@ -380,7 +435,19 @@ class CollectionQueryTool:
380
435
 
381
436
  return self
382
437
 
383
- def ensure_is_modern_api_is_set(func): # pylint: disable=no-self-argument
438
+ def guess_and_set_timeout(self) -> "CollectionQueryTool":
439
+ """
440
+ Try to guess the timeout to use.
441
+ """
442
+
443
+ if PyFunceble.facility.ConfigLoader.is_already_loaded():
444
+ self.timeout = PyFunceble.storage.CONFIGURATION.lookup.timeout
445
+ else:
446
+ self.timeout = self.STD_TIMEOUT
447
+
448
+ return self
449
+
450
+ def ensure_modern_api(func): # pylint: disable=no-self-argument
384
451
  """
385
452
  Ensures that the :code:`is_modern_api` attribute is set before running
386
453
  the decorated method.
@@ -394,7 +461,7 @@ class CollectionQueryTool:
394
461
 
395
462
  return wrapper
396
463
 
397
- @ensure_is_modern_api_is_set
464
+ @ensure_modern_api
398
465
  def pull(self, subject: str) -> Optional[dict]:
399
466
  """
400
467
  Pulls all data related to the subject or :py:class:`None`
@@ -426,6 +493,7 @@ class CollectionQueryTool:
426
493
  response = self.session.post(
427
494
  url,
428
495
  json={"subject": subject},
496
+ timeout=self.timeout,
429
497
  )
430
498
 
431
499
  response_json = response.json()
@@ -452,7 +520,113 @@ class CollectionQueryTool:
452
520
 
453
521
  return None
454
522
 
455
- @ensure_is_modern_api_is_set
523
+ @ensure_modern_api
524
+ def pull_contract(self, amount: int = 1) -> Generator[dict, None, None]:
525
+ """
526
+ Pulls the next amount of contracts.
527
+
528
+ :param int amount:
529
+ The amount of data to pull.
530
+
531
+ :return:
532
+ The response of the query.
533
+ """
534
+
535
+ PyFunceble.facility.Logger.info("Starting to pull next contract")
536
+
537
+ url = f"{self.url_base}/v1/contracts/next"
538
+ params = {
539
+ "limit": amount,
540
+ "shuffle": True,
541
+ }
542
+
543
+ try:
544
+ response = self.session.get(
545
+ url,
546
+ params=params,
547
+ timeout=self.timeout,
548
+ )
549
+
550
+ response_json = response.json()
551
+
552
+ if response.status_code == 200:
553
+ PyFunceble.facility.Logger.debug(
554
+ "Successfully pulled next %r contracts. Response: %r", response_json
555
+ )
556
+
557
+ PyFunceble.facility.Logger.info("Finished to pull next contract")
558
+
559
+ yield response_json
560
+ except (requests.RequestException, json.decoder.JSONDecodeError):
561
+ response_json = [{"subject": {}}]
562
+
563
+ PyFunceble.facility.Logger.debug(
564
+ "Failed to pull next contract. Response: %r", response_json
565
+ )
566
+ PyFunceble.facility.Logger.info("Finished to pull next contracts")
567
+
568
+ yield response_json
569
+
570
+ @ensure_modern_api
571
+ def deliver_contract(self, contract: dict, contract_data: dict) -> Optional[dict]:
572
+ """
573
+ Delivers the given contract data.
574
+
575
+ :param contract:
576
+ The contract to deliver.
577
+ :param contract_data:
578
+ The data to deliver.
579
+
580
+ :return:
581
+ The response of the query.
582
+ """
583
+
584
+ PyFunceble.facility.Logger.info(
585
+ "Starting to deliver contract data: %r", contract
586
+ )
587
+
588
+ contract_id = contract["id"]
589
+ contract_data = (
590
+ contract_data.to_json()
591
+ if not isinstance(contract_data, dict)
592
+ else contract_data
593
+ )
594
+ url = f"{self.url_base}/v1/contracts/{contract_id}/delivery"
595
+
596
+ try:
597
+ response = self.session.post(
598
+ url,
599
+ data=contract_data.encode("utf-8"),
600
+ timeout=self.timeout,
601
+ )
602
+
603
+ response_json = response.json()
604
+
605
+ if response.status_code == 200:
606
+ PyFunceble.facility.Logger.debug(
607
+ "Successfully delivered contract: %r. Response: %r",
608
+ contract_data,
609
+ response_json,
610
+ )
611
+
612
+ PyFunceble.facility.Logger.info(
613
+ "Finished to deliver contract: %r", contract_data
614
+ )
615
+
616
+ return response_json
617
+ except (requests.RequestException, json.decoder.JSONDecodeError):
618
+ response_json = {}
619
+
620
+ PyFunceble.facility.Logger.debug(
621
+ "Failed to deliver contract: %r. Response: %r", contract_data, response_json
622
+ )
623
+ PyFunceble.facility.Logger.info(
624
+ "Finished to deliver contract: %r", contract_data
625
+ )
626
+
627
+ return None
628
+
629
+ @ensure_modern_api
456
630
  def push(
457
631
  self,
458
632
  checker_status: Union[
@@ -574,11 +748,26 @@ class CollectionQueryTool:
574
748
  response = self.session.post(
575
749
  url,
576
750
  json=data,
751
+ timeout=self.timeout,
752
+ )
753
+ elif isinstance(
754
+ data,
755
+ (
756
+ AvailabilityCheckerStatus,
757
+ SyntaxCheckerStatus,
758
+ ReputationCheckerStatus,
759
+ ),
760
+ ):
761
+ response = self.session.post(
762
+ url,
763
+ json=data.to_dict(),
764
+ timeout=self.timeout,
577
765
  )
578
766
  else:
579
767
  response = self.session.post(
580
768
  url,
581
769
  data=data,
770
+ timeout=self.timeout,
582
771
  )
583
772
 
584
773
  response_json = response.json()
@@ -622,6 +811,12 @@ class CollectionQueryTool:
622
811
  if not self.token:
623
812
  return None
624
813
 
814
+ if isinstance(
815
+ data,
816
+ (AvailabilityCheckerStatus, SyntaxCheckerStatus, ReputationCheckerStatus),
817
+ ):
818
+ data = data.to_dict()
819
+
625
820
  if not isinstance(data, dict): # pragma: no cover ## Should never happen
626
821
  raise TypeError(f"<data> should be {dict}, {type(data)} given.")
627
822
 
@@ -633,6 +828,7 @@ class CollectionQueryTool:
633
828
  response = self.session.post(
634
829
  url,
635
830
  json=data,
831
+ timeout=self.timeout,
636
832
  )
637
833
 
638
834
  response_json = response.json()
@@ -113,12 +113,12 @@ class RequestHTTPSAdapter(RequestAdapterBase):
113
113
  )
114
114
 
115
115
  if parsed_url.scheme == "https":
116
- self.poolmanager.connection_pool_kw[
117
- "server_hostname"
118
- ] = parsed_url.hostname
119
- self.poolmanager.connection_pool_kw[
120
- "assert_hostname"
121
- ] = parsed_url.hostname
116
+ self.poolmanager.connection_pool_kw["server_hostname"] = (
117
+ parsed_url.hostname
118
+ )
119
+ self.poolmanager.connection_pool_kw["assert_hostname"] = (
120
+ parsed_url.hostname
121
+ )
122
122
 
123
123
  # Ensure that the Hosts header is present. Otherwise, connection might
124
124
  # not work.
@@ -49,6 +49,7 @@ License:
49
49
  See the License for the specific language governing permissions and
50
50
  limitations under the License.
51
51
  """
52
+
52
53
  # pylint: enable=line-too-long
53
54
 
54
55
  from typing import Any
PyFunceble/storage.py CHANGED
@@ -61,7 +61,7 @@ from dotenv import load_dotenv
61
61
  from PyFunceble.storage_facility import get_config_directory
62
62
 
63
63
  PROJECT_NAME: str = "PyFunceble"
64
- PROJECT_VERSION: str = "4.2.7.dev (Blue Duckling: Ixora)"
64
+ PROJECT_VERSION: str = "4.2.10.dev (Blue Duckling: Ixora)"
65
65
 
66
66
  DISTRIBUTED_CONFIGURATION_FILENAME: str = ".PyFunceble_production.yaml"
67
67
  DISTRIBUTED_DIR_STRUCTURE_FILENAME: str = "dir_structure_production.json"
@@ -79,7 +79,9 @@ IPV4_REPUTATION_FILENAME: str = "ipv4_reputation.data"
79
79
  IANA_DUMP_LINK: str = (
80
80
  "https://raw.githubusercontent.com/PyFunceble/iana/master/iana-domains-db.json"
81
81
  )
82
- PUBLIC_SUFFIX_DUMP_LINK: str = "https://raw.githubusercontent.com/PyFunceble/public-suffix/master/public-suffix.json"
82
+ PUBLIC_SUFFIX_DUMP_LINK: str = (
83
+ "https://raw.githubusercontent.com/PyFunceble/public-suffix/master/public-suffix.json"
84
+ )
83
85
  USER_AGENT_DUMP_LINK: str = (
84
86
  "https://raw.githubusercontent.com/PyFunceble/user_agents/master/user_agents.json"
85
87
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyFunceble-dev
3
- Version: 4.2.7
3
+ Version: 4.2.10
4
4
  Summary: The tool to check the availability or syntax of domain, IP or URL.
5
5
  Home-page: https://github.com/funilrys/PyFunceble
6
6
  Author: funilrys
@@ -21,74 +21,74 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: License :: OSI Approved
22
22
  Requires-Python: >=3.8, <4
23
23
  License-File: LICENSE
24
- Requires-Dist: inflection
24
+ Requires-Dist: dnspython[doh] ~=2.6.0
25
+ Requires-Dist: shtab
25
26
  Requires-Dist: domain2idna ~=1.12.0
26
- Requires-Dist: python-dotenv
27
- Requires-Dist: dnspython[doh] ~=2.0.0
27
+ Requires-Dist: inflection
28
28
  Requires-Dist: PyMySQL
29
- Requires-Dist: python-box[all] ~=6.0.0
29
+ Requires-Dist: SQLAlchemy ~=2.0
30
+ Requires-Dist: python-dotenv
31
+ Requires-Dist: colorama
32
+ Requires-Dist: alembic
30
33
  Requires-Dist: requests[socks] <3
31
- Requires-Dist: cryptography >=3.3.2
34
+ Requires-Dist: setuptools >=65.5.1
32
35
  Requires-Dist: packaging
33
36
  Requires-Dist: PyYAML
34
- Requires-Dist: shtab
35
- Requires-Dist: alembic
36
- Requires-Dist: setuptools >=65.5.1
37
- Requires-Dist: SQLAlchemy ~=2.0
38
- Requires-Dist: colorama
37
+ Requires-Dist: python-box[all] ~=6.0.0
38
+ Requires-Dist: cryptography ~=42.0
39
39
  Provides-Extra: dev
40
- Requires-Dist: flake8 ; extra == 'dev'
41
40
  Requires-Dist: isort ; extra == 'dev'
42
- Requires-Dist: pylint ; extra == 'dev'
43
41
  Requires-Dist: black ; extra == 'dev'
42
+ Requires-Dist: pylint ; extra == 'dev'
43
+ Requires-Dist: flake8 ; extra == 'dev'
44
44
  Provides-Extra: docs
45
45
  Requires-Dist: alabaster <0.8,>=0.7 ; extra == 'docs'
46
- Requires-Dist: sphinx >=3.4.3 ; extra == 'docs'
47
- Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
48
46
  Requires-Dist: Pygments >=2.0 ; extra == 'docs'
47
+ Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
48
+ Requires-Dist: sphinx >=3.4.3 ; extra == 'docs'
49
49
  Provides-Extra: full
50
- Requires-Dist: alembic ; extra == 'full'
51
50
  Requires-Dist: PyMySQL ; extra == 'full'
52
- Requires-Dist: sphinx-rtd-theme ; extra == 'full'
53
51
  Requires-Dist: isort ; extra == 'full'
54
- Requires-Dist: black ; extra == 'full'
52
+ Requires-Dist: Pygments >=2.0 ; extra == 'full'
55
53
  Requires-Dist: flake8 ; extra == 'full'
56
- Requires-Dist: tox ; extra == 'full'
57
- Requires-Dist: dnspython[doh] ~=2.0.0 ; extra == 'full'
58
- Requires-Dist: python-box[all] ~=6.0.0 ; extra == 'full'
59
- Requires-Dist: cryptography >=3.3.2 ; extra == 'full'
60
- Requires-Dist: packaging ; extra == 'full'
61
- Requires-Dist: coverage ; extra == 'full'
62
- Requires-Dist: setuptools >=65.5.1 ; extra == 'full'
63
54
  Requires-Dist: alabaster <0.8,>=0.7 ; extra == 'full'
64
- Requires-Dist: colorama ; extra == 'full'
65
- Requires-Dist: pylint ; extra == 'full'
55
+ Requires-Dist: dnspython[doh] ~=2.6.0 ; extra == 'full'
66
56
  Requires-Dist: domain2idna ~=1.12.0 ; extra == 'full'
67
57
  Requires-Dist: python-dotenv ; extra == 'full'
58
+ Requires-Dist: colorama ; extra == 'full'
59
+ Requires-Dist: packaging ; extra == 'full'
60
+ Requires-Dist: pylint ; extra == 'full'
61
+ Requires-Dist: tox ; extra == 'full'
62
+ Requires-Dist: black ; extra == 'full'
63
+ Requires-Dist: coverage ; extra == 'full'
68
64
  Requires-Dist: requests[socks] <3 ; extra == 'full'
69
- Requires-Dist: SQLAlchemy ~=2.0 ; extra == 'full'
65
+ Requires-Dist: PyYAML ; extra == 'full'
66
+ Requires-Dist: python-box[all] ~=6.0.0 ; extra == 'full'
67
+ Requires-Dist: shtab ; extra == 'full'
70
68
  Requires-Dist: sphinx >=3.4.3 ; extra == 'full'
71
- Requires-Dist: Pygments >=2.0 ; extra == 'full'
69
+ Requires-Dist: SQLAlchemy ~=2.0 ; extra == 'full'
72
70
  Requires-Dist: inflection ; extra == 'full'
73
- Requires-Dist: shtab ; extra == 'full'
74
- Requires-Dist: PyYAML ; extra == 'full'
71
+ Requires-Dist: alembic ; extra == 'full'
72
+ Requires-Dist: setuptools >=65.5.1 ; extra == 'full'
73
+ Requires-Dist: sphinx-rtd-theme ; extra == 'full'
74
+ Requires-Dist: cryptography ~=42.0 ; extra == 'full'
75
75
  Provides-Extra: psql
76
- Requires-Dist: inflection ; extra == 'psql'
76
+ Requires-Dist: dnspython[doh] ~=2.6.0 ; extra == 'psql'
77
+ Requires-Dist: shtab ; extra == 'psql'
77
78
  Requires-Dist: domain2idna ~=1.12.0 ; extra == 'psql'
78
- Requires-Dist: python-dotenv ; extra == 'psql'
79
- Requires-Dist: psycopg2 ; extra == 'psql'
80
- Requires-Dist: dnspython[doh] ~=2.0.0 ; extra == 'psql'
79
+ Requires-Dist: inflection ; extra == 'psql'
81
80
  Requires-Dist: PyMySQL ; extra == 'psql'
82
- Requires-Dist: python-box[all] ~=6.0.0 ; extra == 'psql'
81
+ Requires-Dist: SQLAlchemy ~=2.0 ; extra == 'psql'
82
+ Requires-Dist: python-dotenv ; extra == 'psql'
83
+ Requires-Dist: colorama ; extra == 'psql'
84
+ Requires-Dist: alembic ; extra == 'psql'
83
85
  Requires-Dist: requests[socks] <3 ; extra == 'psql'
84
- Requires-Dist: cryptography >=3.3.2 ; extra == 'psql'
86
+ Requires-Dist: setuptools >=65.5.1 ; extra == 'psql'
87
+ Requires-Dist: psycopg2 ; extra == 'psql'
85
88
  Requires-Dist: packaging ; extra == 'psql'
86
89
  Requires-Dist: PyYAML ; extra == 'psql'
87
- Requires-Dist: shtab ; extra == 'psql'
88
- Requires-Dist: alembic ; extra == 'psql'
89
- Requires-Dist: setuptools >=65.5.1 ; extra == 'psql'
90
- Requires-Dist: SQLAlchemy ~=2.0 ; extra == 'psql'
91
- Requires-Dist: colorama ; extra == 'psql'
90
+ Requires-Dist: python-box[all] ~=6.0.0 ; extra == 'psql'
91
+ Requires-Dist: cryptography ~=42.0 ; extra == 'psql'
92
92
  Provides-Extra: test
93
93
  Requires-Dist: tox ; extra == 'test'
94
94
  Requires-Dist: coverage ; extra == 'test'