koichi12 commited on
Commit
30150f7
·
verified ·
1 Parent(s): 5dbe6b9

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/INSTALLER +1 -0
  2. .venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/METADATA +108 -0
  3. .venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/RECORD +9 -0
  4. .venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/WHEEL +4 -0
  5. .venv/lib/python3.11/site-packages/importlib_metadata/__init__.py +1132 -0
  6. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/__init__.cpython-311.pyc +0 -0
  7. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_adapters.cpython-311.pyc +0 -0
  8. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_collections.cpython-311.pyc +0 -0
  9. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_compat.cpython-311.pyc +0 -0
  10. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_functools.cpython-311.pyc +0 -0
  11. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_itertools.cpython-311.pyc +0 -0
  12. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_meta.cpython-311.pyc +0 -0
  13. .venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/diagnose.cpython-311.pyc +0 -0
  14. .venv/lib/python3.11/site-packages/importlib_metadata/_adapters.py +135 -0
  15. .venv/lib/python3.11/site-packages/importlib_metadata/_collections.py +30 -0
  16. .venv/lib/python3.11/site-packages/importlib_metadata/_compat.py +56 -0
  17. .venv/lib/python3.11/site-packages/importlib_metadata/_functools.py +104 -0
  18. .venv/lib/python3.11/site-packages/importlib_metadata/_itertools.py +171 -0
  19. .venv/lib/python3.11/site-packages/importlib_metadata/_meta.py +75 -0
  20. .venv/lib/python3.11/site-packages/importlib_metadata/_text.py +99 -0
  21. .venv/lib/python3.11/site-packages/importlib_metadata/compat/__init__.py +0 -0
  22. .venv/lib/python3.11/site-packages/importlib_metadata/compat/__pycache__/py39.cpython-311.pyc +0 -0
  23. .venv/lib/python3.11/site-packages/importlib_metadata/compat/py311.py +22 -0
  24. .venv/lib/python3.11/site-packages/importlib_metadata/compat/py39.py +36 -0
  25. .venv/lib/python3.11/site-packages/importlib_metadata/diagnose.py +21 -0
  26. .venv/lib/python3.11/site-packages/importlib_metadata/py.typed +0 -0
  27. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/__init__.cpython-311.pyc +0 -0
  28. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc +0 -0
  29. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/_subprocess.cpython-311.pyc +0 -0
  30. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/_types.cpython-311.pyc +0 -0
  31. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/config.cpython-311.pyc +0 -0
  32. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/importer.cpython-311.pyc +0 -0
  33. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/logging.cpython-311.pyc +0 -0
  34. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/main.cpython-311.pyc +0 -0
  35. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/server.cpython-311.pyc +0 -0
  36. .venv/lib/python3.11/site-packages/uvicorn/__pycache__/workers.cpython-311.pyc +0 -0
  37. .venv/lib/python3.11/site-packages/uvicorn/lifespan/__init__.py +0 -0
  38. .venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/__init__.cpython-311.pyc +0 -0
  39. .venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/off.cpython-311.pyc +0 -0
  40. .venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/on.cpython-311.pyc +0 -0
  41. .venv/lib/python3.11/site-packages/uvicorn/lifespan/off.py +17 -0
  42. .venv/lib/python3.11/site-packages/uvicorn/lifespan/on.py +137 -0
  43. .venv/lib/python3.11/site-packages/uvicorn/loops/__pycache__/__init__.cpython-311.pyc +0 -0
  44. .venv/lib/python3.11/site-packages/uvicorn/protocols/__init__.py +0 -0
  45. .venv/lib/python3.11/site-packages/uvicorn/protocols/__pycache__/__init__.cpython-311.pyc +0 -0
  46. .venv/lib/python3.11/site-packages/uvicorn/protocols/__pycache__/utils.cpython-311.pyc +0 -0
  47. .venv/lib/python3.11/site-packages/uvicorn/protocols/http/__init__.py +0 -0
  48. .venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/__init__.cpython-311.pyc +0 -0
  49. .venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/auto.cpython-311.pyc +0 -0
  50. .venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/flow_control.cpython-311.pyc +0 -0
.venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/METADATA ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: blake3
3
+ Version: 1.0.4
4
+ Summary: Python bindings for the Rust blake3 crate
5
+ Home-Page: https://github.com/oconnor663/blake3-py
6
+ Author: Jack O'Connor <oconnor663@gmail.com>
7
+ Author-email: Jack O'Connor <oconnor663@gmail.com>
8
+ License: CC0-1.0 OR Apache-2.0
9
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
10
+ Project-URL: Source Code, https://github.com/oconnor663/blake3-py
11
+
12
+ # blake3-py [![tests](https://github.com/oconnor663/blake3-py/actions/workflows/tests.yml/badge.svg?branch=master&event=push)](https://github.com/oconnor663/blake3-py/actions/workflows/tests.yml) [![PyPI version](https://badge.fury.io/py/blake3.svg)](https://pypi.python.org/pypi/blake3)
13
+
14
+ Python bindings for the [official Rust implementation of
15
+ BLAKE3](https://github.com/BLAKE3-team/BLAKE3), based on
16
+ [PyO3](https://github.com/PyO3/pyo3). These bindings expose all the features of
17
+ BLAKE3, including extendable output, keying, and multithreading. The basic API
18
+ matches that of Python's standard
19
+ [`hashlib`](https://docs.python.org/3/library/hashlib.html) module.
20
+
21
+ ## Examples
22
+
23
+ ```python
24
+ from blake3 import blake3
25
+
26
+ # Hash some input all at once. The input can be bytes, a bytearray, or a memoryview.
27
+ hash1 = blake3(b"foobarbaz").digest()
28
+
29
+ # Hash the same input incrementally.
30
+ hasher = blake3()
31
+ hasher.update(b"foo")
32
+ hasher.update(b"bar")
33
+ hasher.update(b"baz")
34
+ hash2 = hasher.digest()
35
+ assert hash1 == hash2
36
+
37
+ # Hash the same input fluently.
38
+ assert hash1 == blake3(b"foo").update(b"bar").update(b"baz").digest()
39
+
40
+ # Hexadecimal output.
41
+ print("The hash of 'hello world' is", blake3(b"hello world").hexdigest())
42
+
43
+ # Use the keyed hashing mode, which takes a 32-byte key.
44
+ import secrets
45
+ random_key = secrets.token_bytes(32)
46
+ message = b"a message to authenticate"
47
+ mac = blake3(message, key=random_key).digest()
48
+
49
+ # Use the key derivation mode, which takes a context string. Context strings
50
+ # should be hardcoded, globally unique, and application-specific.
51
+ context = "blake3-py 2020-03-04 11:13:10 example context"
52
+ key_material = b"usually at least 32 random bytes, not a password"
53
+ derived_key = blake3(key_material, derive_key_context=context).digest()
54
+
55
+ # Extendable output. The default digest size is 32 bytes.
56
+ extended = blake3(b"foo").digest(length=100)
57
+ assert extended[:32] == blake3(b"foo").digest()
58
+ assert extended[75:100] == blake3(b"foo").digest(length=25, seek=75)
59
+
60
+ # Hash a large input using multiple threads. Note that this can be slower for
61
+ # inputs shorter than ~1 MB, and it's a good idea to benchmark it for your use
62
+ # case on your platform.
63
+ large_input = bytearray(1_000_000)
64
+ hash_single = blake3(large_input).digest()
65
+ hash_two = blake3(large_input, max_threads=2).digest()
66
+ hash_many = blake3(large_input, max_threads=blake3.AUTO).digest()
67
+ assert hash_single == hash_two == hash_many
68
+
69
+ # Hash a file with multiple threads using memory mapping. This is what b3sum
70
+ # does by default.
71
+ file_hasher = blake3(max_threads=blake3.AUTO)
72
+ file_hasher.update_mmap("/big/file.txt")
73
+ file_hash = file_hasher.digest()
74
+
75
+ # Copy a hasher that's already accepted some input.
76
+ hasher1 = blake3(b"foo")
77
+ hasher2 = hasher1.copy()
78
+ hasher1.update(b"bar")
79
+ hasher2.update(b"baz")
80
+ assert hasher1.digest() == blake3(b"foobar").digest()
81
+ assert hasher2.digest() == blake3(b"foobaz").digest()
82
+ ```
83
+
84
+ ## Installation
85
+
86
+ ```
87
+ pip install blake3
88
+ ```
89
+
90
+ As usual with Pip, you might need to use `sudo` or the `--user` flag
91
+ with the command above, depending on how you installed Python on your
92
+ system.
93
+
94
+ There are binary wheels [available on
95
+ PyPI](https://pypi.org/project/blake3/#files) for most environments. But
96
+ if you're building the source distribution, or if a binary wheel isn't
97
+ available for your environment, you'll need to [install the Rust
98
+ toolchain](https://rustup.rs).
99
+
100
+ ## C Bindings
101
+
102
+ Experimental bindings for the official BLAKE3 C implementation are available in
103
+ the [`c_impl`](c_impl) directory. These will probably not be published on PyPI,
104
+ and most applications should prefer the Rust-based bindings. But if you can't
105
+ depend on the Rust toolchain, and you're on some platform that this project
106
+ doesn't provide binary wheels for, the C-based bindings might be an
107
+ alternative.
108
+
.venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/RECORD ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ blake3-1.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ blake3-1.0.4.dist-info/METADATA,sha256=t8zfV6RI_1YtG_N6JgFUG1Z73sK6n31N9bv0rejh4eY,4166
3
+ blake3-1.0.4.dist-info/RECORD,,
4
+ blake3-1.0.4.dist-info/WHEEL,sha256=eKn-h6LbuPin9BQdctwIkEq1OLRtDcdOVrhrYyXn53g,129
5
+ blake3/__init__.py,sha256=i5GXKa35g4Dt_hOK8OmCFGY-6xDtzmTAGlepSFv_0ns,107
6
+ blake3/__init__.pyi,sha256=Ngl-UCmwX3q3E9IWmQGCqbEQhfdkaoSVd1G1QaHtQNg,750
7
+ blake3/__pycache__/__init__.cpython-311.pyc,,
8
+ blake3/blake3.cpython-311-x86_64-linux-gnu.so,sha256=hZ7I6d_EChuUSlcKn5QfaNN0TDjYNNp0S9xedZe5vec,964720
9
+ blake3/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
.venv/lib/python3.11/site-packages/blake3-1.0.4.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.8.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64
.venv/lib/python3.11/site-packages/importlib_metadata/__init__.py ADDED
@@ -0,0 +1,1132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ APIs exposing metadata from third-party Python packages.
3
+
4
+ This codebase is shared between importlib.metadata in the stdlib
5
+ and importlib_metadata in PyPI. See
6
+ https://github.com/python/importlib_metadata/wiki/Development-Methodology
7
+ for more detail.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import abc
13
+ import collections
14
+ import email
15
+ import functools
16
+ import itertools
17
+ import operator
18
+ import os
19
+ import pathlib
20
+ import posixpath
21
+ import re
22
+ import sys
23
+ import textwrap
24
+ import types
25
+ from contextlib import suppress
26
+ from importlib import import_module
27
+ from importlib.abc import MetaPathFinder
28
+ from itertools import starmap
29
+ from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast
30
+
31
+ from . import _meta
32
+ from ._collections import FreezableDefaultDict, Pair
33
+ from ._compat import (
34
+ NullFinder,
35
+ install,
36
+ )
37
+ from ._functools import method_cache, pass_none
38
+ from ._itertools import always_iterable, bucket, unique_everseen
39
+ from ._meta import PackageMetadata, SimplePath
40
+ from .compat import py39, py311
41
+
42
+ __all__ = [
43
+ 'Distribution',
44
+ 'DistributionFinder',
45
+ 'PackageMetadata',
46
+ 'PackageNotFoundError',
47
+ 'SimplePath',
48
+ 'distribution',
49
+ 'distributions',
50
+ 'entry_points',
51
+ 'files',
52
+ 'metadata',
53
+ 'packages_distributions',
54
+ 'requires',
55
+ 'version',
56
+ ]
57
+
58
+
59
+ class PackageNotFoundError(ModuleNotFoundError):
60
+ """The package was not found."""
61
+
62
+ def __str__(self) -> str:
63
+ return f"No package metadata was found for {self.name}"
64
+
65
+ @property
66
+ def name(self) -> str: # type: ignore[override] # make readonly
67
+ (name,) = self.args
68
+ return name
69
+
70
+
71
+ class Sectioned:
72
+ """
73
+ A simple entry point config parser for performance
74
+
75
+ >>> for item in Sectioned.read(Sectioned._sample):
76
+ ... print(item)
77
+ Pair(name='sec1', value='# comments ignored')
78
+ Pair(name='sec1', value='a = 1')
79
+ Pair(name='sec1', value='b = 2')
80
+ Pair(name='sec2', value='a = 2')
81
+
82
+ >>> res = Sectioned.section_pairs(Sectioned._sample)
83
+ >>> item = next(res)
84
+ >>> item.name
85
+ 'sec1'
86
+ >>> item.value
87
+ Pair(name='a', value='1')
88
+ >>> item = next(res)
89
+ >>> item.value
90
+ Pair(name='b', value='2')
91
+ >>> item = next(res)
92
+ >>> item.name
93
+ 'sec2'
94
+ >>> item.value
95
+ Pair(name='a', value='2')
96
+ >>> list(res)
97
+ []
98
+ """
99
+
100
+ _sample = textwrap.dedent(
101
+ """
102
+ [sec1]
103
+ # comments ignored
104
+ a = 1
105
+ b = 2
106
+
107
+ [sec2]
108
+ a = 2
109
+ """
110
+ ).lstrip()
111
+
112
+ @classmethod
113
+ def section_pairs(cls, text):
114
+ return (
115
+ section._replace(value=Pair.parse(section.value))
116
+ for section in cls.read(text, filter_=cls.valid)
117
+ if section.name is not None
118
+ )
119
+
120
+ @staticmethod
121
+ def read(text, filter_=None):
122
+ lines = filter(filter_, map(str.strip, text.splitlines()))
123
+ name = None
124
+ for value in lines:
125
+ section_match = value.startswith('[') and value.endswith(']')
126
+ if section_match:
127
+ name = value.strip('[]')
128
+ continue
129
+ yield Pair(name, value)
130
+
131
+ @staticmethod
132
+ def valid(line: str):
133
+ return line and not line.startswith('#')
134
+
135
+
136
+ class EntryPoint:
137
+ """An entry point as defined by Python packaging conventions.
138
+
139
+ See `the packaging docs on entry points
140
+ <https://packaging.python.org/specifications/entry-points/>`_
141
+ for more information.
142
+
143
+ >>> ep = EntryPoint(
144
+ ... name=None, group=None, value='package.module:attr [extra1, extra2]')
145
+ >>> ep.module
146
+ 'package.module'
147
+ >>> ep.attr
148
+ 'attr'
149
+ >>> ep.extras
150
+ ['extra1', 'extra2']
151
+ """
152
+
153
+ pattern = re.compile(
154
+ r'(?P<module>[\w.]+)\s*'
155
+ r'(:\s*(?P<attr>[\w.]+)\s*)?'
156
+ r'((?P<extras>\[.*\])\s*)?$'
157
+ )
158
+ """
159
+ A regular expression describing the syntax for an entry point,
160
+ which might look like:
161
+
162
+ - module
163
+ - package.module
164
+ - package.module:attribute
165
+ - package.module:object.attribute
166
+ - package.module:attr [extra1, extra2]
167
+
168
+ Other combinations are possible as well.
169
+
170
+ The expression is lenient about whitespace around the ':',
171
+ following the attr, and following any extras.
172
+ """
173
+
174
+ name: str
175
+ value: str
176
+ group: str
177
+
178
+ dist: Optional[Distribution] = None
179
+
180
+ def __init__(self, name: str, value: str, group: str) -> None:
181
+ vars(self).update(name=name, value=value, group=group)
182
+
183
+ def load(self) -> Any:
184
+ """Load the entry point from its definition. If only a module
185
+ is indicated by the value, return that module. Otherwise,
186
+ return the named object.
187
+ """
188
+ match = cast(Match, self.pattern.match(self.value))
189
+ module = import_module(match.group('module'))
190
+ attrs = filter(None, (match.group('attr') or '').split('.'))
191
+ return functools.reduce(getattr, attrs, module)
192
+
193
+ @property
194
+ def module(self) -> str:
195
+ match = self.pattern.match(self.value)
196
+ assert match is not None
197
+ return match.group('module')
198
+
199
+ @property
200
+ def attr(self) -> str:
201
+ match = self.pattern.match(self.value)
202
+ assert match is not None
203
+ return match.group('attr')
204
+
205
+ @property
206
+ def extras(self) -> List[str]:
207
+ match = self.pattern.match(self.value)
208
+ assert match is not None
209
+ return re.findall(r'\w+', match.group('extras') or '')
210
+
211
+ def _for(self, dist):
212
+ vars(self).update(dist=dist)
213
+ return self
214
+
215
+ def matches(self, **params):
216
+ """
217
+ EntryPoint matches the given parameters.
218
+
219
+ >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
220
+ >>> ep.matches(group='foo')
221
+ True
222
+ >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
223
+ True
224
+ >>> ep.matches(group='foo', name='other')
225
+ False
226
+ >>> ep.matches()
227
+ True
228
+ >>> ep.matches(extras=['extra1', 'extra2'])
229
+ True
230
+ >>> ep.matches(module='bing')
231
+ True
232
+ >>> ep.matches(attr='bong')
233
+ True
234
+ """
235
+ self._disallow_dist(params)
236
+ attrs = (getattr(self, param) for param in params)
237
+ return all(map(operator.eq, params.values(), attrs))
238
+
239
+ @staticmethod
240
+ def _disallow_dist(params):
241
+ """
242
+ Querying by dist is not allowed (dist objects are not comparable).
243
+ >>> EntryPoint(name='fan', value='fav', group='fag').matches(dist='foo')
244
+ Traceback (most recent call last):
245
+ ...
246
+ ValueError: "dist" is not suitable for matching...
247
+ """
248
+ if "dist" in params:
249
+ raise ValueError(
250
+ '"dist" is not suitable for matching. '
251
+ "Instead, use Distribution.entry_points.select() on a "
252
+ "located distribution."
253
+ )
254
+
255
+ def _key(self):
256
+ return self.name, self.value, self.group
257
+
258
+ def __lt__(self, other):
259
+ return self._key() < other._key()
260
+
261
+ def __eq__(self, other):
262
+ return self._key() == other._key()
263
+
264
+ def __setattr__(self, name, value):
265
+ raise AttributeError("EntryPoint objects are immutable.")
266
+
267
+ def __repr__(self):
268
+ return (
269
+ f'EntryPoint(name={self.name!r}, value={self.value!r}, '
270
+ f'group={self.group!r})'
271
+ )
272
+
273
+ def __hash__(self) -> int:
274
+ return hash(self._key())
275
+
276
+
277
+ class EntryPoints(tuple):
278
+ """
279
+ An immutable collection of selectable EntryPoint objects.
280
+ """
281
+
282
+ __slots__ = ()
283
+
284
+ def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] # Work with str instead of int
285
+ """
286
+ Get the EntryPoint in self matching name.
287
+ """
288
+ try:
289
+ return next(iter(self.select(name=name)))
290
+ except StopIteration:
291
+ raise KeyError(name)
292
+
293
+ def __repr__(self):
294
+ """
295
+ Repr with classname and tuple constructor to
296
+ signal that we deviate from regular tuple behavior.
297
+ """
298
+ return '%s(%r)' % (self.__class__.__name__, tuple(self))
299
+
300
+ def select(self, **params) -> EntryPoints:
301
+ """
302
+ Select entry points from self that match the
303
+ given parameters (typically group and/or name).
304
+ """
305
+ return EntryPoints(ep for ep in self if py39.ep_matches(ep, **params))
306
+
307
+ @property
308
+ def names(self) -> Set[str]:
309
+ """
310
+ Return the set of all names of all entry points.
311
+ """
312
+ return {ep.name for ep in self}
313
+
314
+ @property
315
+ def groups(self) -> Set[str]:
316
+ """
317
+ Return the set of all groups of all entry points.
318
+ """
319
+ return {ep.group for ep in self}
320
+
321
+ @classmethod
322
+ def _from_text_for(cls, text, dist):
323
+ return cls(ep._for(dist) for ep in cls._from_text(text))
324
+
325
+ @staticmethod
326
+ def _from_text(text):
327
+ return (
328
+ EntryPoint(name=item.value.name, value=item.value.value, group=item.name)
329
+ for item in Sectioned.section_pairs(text or '')
330
+ )
331
+
332
+
333
+ class PackagePath(pathlib.PurePosixPath):
334
+ """A reference to a path in a package"""
335
+
336
+ hash: Optional[FileHash]
337
+ size: int
338
+ dist: Distribution
339
+
340
+ def read_text(self, encoding: str = 'utf-8') -> str:
341
+ return self.locate().read_text(encoding=encoding)
342
+
343
+ def read_binary(self) -> bytes:
344
+ return self.locate().read_bytes()
345
+
346
+ def locate(self) -> SimplePath:
347
+ """Return a path-like object for this path"""
348
+ return self.dist.locate_file(self)
349
+
350
+
351
+ class FileHash:
352
+ def __init__(self, spec: str) -> None:
353
+ self.mode, _, self.value = spec.partition('=')
354
+
355
+ def __repr__(self) -> str:
356
+ return f'<FileHash mode: {self.mode} value: {self.value}>'
357
+
358
+
359
+ class Distribution(metaclass=abc.ABCMeta):
360
+ """
361
+ An abstract Python distribution package.
362
+
363
+ Custom providers may derive from this class and define
364
+ the abstract methods to provide a concrete implementation
365
+ for their environment. Some providers may opt to override
366
+ the default implementation of some properties to bypass
367
+ the file-reading mechanism.
368
+ """
369
+
370
+ @abc.abstractmethod
371
+ def read_text(self, filename) -> Optional[str]:
372
+ """Attempt to load metadata file given by the name.
373
+
374
+ Python distribution metadata is organized by blobs of text
375
+ typically represented as "files" in the metadata directory
376
+ (e.g. package-1.0.dist-info). These files include things
377
+ like:
378
+
379
+ - METADATA: The distribution metadata including fields
380
+ like Name and Version and Description.
381
+ - entry_points.txt: A series of entry points as defined in
382
+ `the entry points spec <https://packaging.python.org/en/latest/specifications/entry-points/#file-format>`_.
383
+ - RECORD: A record of files according to
384
+ `this recording spec <https://packaging.python.org/en/latest/specifications/recording-installed-packages/#the-record-file>`_.
385
+
386
+ A package may provide any set of files, including those
387
+ not listed here or none at all.
388
+
389
+ :param filename: The name of the file in the distribution info.
390
+ :return: The text if found, otherwise None.
391
+ """
392
+
393
+ @abc.abstractmethod
394
+ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
395
+ """
396
+ Given a path to a file in this distribution, return a SimplePath
397
+ to it.
398
+
399
+ This method is used by callers of ``Distribution.files()`` to
400
+ locate files within the distribution. If it's possible for a
401
+ Distribution to represent files in the distribution as
402
+ ``SimplePath`` objects, it should implement this method
403
+ to resolve such objects.
404
+
405
+ Some Distribution providers may elect not to resolve SimplePath
406
+ objects within the distribution by raising a
407
+ NotImplementedError, but consumers of such a Distribution would
408
+ be unable to invoke ``Distribution.files()``.
409
+ """
410
+
411
+ @classmethod
412
+ def from_name(cls, name: str) -> Distribution:
413
+ """Return the Distribution for the given package name.
414
+
415
+ :param name: The name of the distribution package to search for.
416
+ :return: The Distribution instance (or subclass thereof) for the named
417
+ package, if found.
418
+ :raises PackageNotFoundError: When the named package's distribution
419
+ metadata cannot be found.
420
+ :raises ValueError: When an invalid value is supplied for name.
421
+ """
422
+ if not name:
423
+ raise ValueError("A distribution name is required.")
424
+ try:
425
+ return next(iter(cls._prefer_valid(cls.discover(name=name))))
426
+ except StopIteration:
427
+ raise PackageNotFoundError(name)
428
+
429
+ @classmethod
430
+ def discover(
431
+ cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs
432
+ ) -> Iterable[Distribution]:
433
+ """Return an iterable of Distribution objects for all packages.
434
+
435
+ Pass a ``context`` or pass keyword arguments for constructing
436
+ a context.
437
+
438
+ :context: A ``DistributionFinder.Context`` object.
439
+ :return: Iterable of Distribution objects for packages matching
440
+ the context.
441
+ """
442
+ if context and kwargs:
443
+ raise ValueError("cannot accept context and kwargs")
444
+ context = context or DistributionFinder.Context(**kwargs)
445
+ return itertools.chain.from_iterable(
446
+ resolver(context) for resolver in cls._discover_resolvers()
447
+ )
448
+
449
+ @staticmethod
450
+ def _prefer_valid(dists: Iterable[Distribution]) -> Iterable[Distribution]:
451
+ """
452
+ Prefer (move to the front) distributions that have metadata.
453
+
454
+ Ref python/importlib_resources#489.
455
+ """
456
+ buckets = bucket(dists, lambda dist: bool(dist.metadata))
457
+ return itertools.chain(buckets[True], buckets[False])
458
+
459
+ @staticmethod
460
+ def at(path: str | os.PathLike[str]) -> Distribution:
461
+ """Return a Distribution for the indicated metadata path.
462
+
463
+ :param path: a string or path-like object
464
+ :return: a concrete Distribution instance for the path
465
+ """
466
+ return PathDistribution(pathlib.Path(path))
467
+
468
+ @staticmethod
469
+ def _discover_resolvers():
470
+ """Search the meta_path for resolvers (MetadataPathFinders)."""
471
+ declared = (
472
+ getattr(finder, 'find_distributions', None) for finder in sys.meta_path
473
+ )
474
+ return filter(None, declared)
475
+
476
+ @property
477
+ def metadata(self) -> _meta.PackageMetadata:
478
+ """Return the parsed metadata for this Distribution.
479
+
480
+ The returned object will have keys that name the various bits of
481
+ metadata per the
482
+ `Core metadata specifications <https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata>`_.
483
+
484
+ Custom providers may provide the METADATA file or override this
485
+ property.
486
+ """
487
+ # deferred for performance (python/cpython#109829)
488
+ from . import _adapters
489
+
490
+ opt_text = (
491
+ self.read_text('METADATA')
492
+ or self.read_text('PKG-INFO')
493
+ # This last clause is here to support old egg-info files. Its
494
+ # effect is to just end up using the PathDistribution's self._path
495
+ # (which points to the egg-info file) attribute unchanged.
496
+ or self.read_text('')
497
+ )
498
+ text = cast(str, opt_text)
499
+ return _adapters.Message(email.message_from_string(text))
500
+
501
+ @property
502
+ def name(self) -> str:
503
+ """Return the 'Name' metadata for the distribution package."""
504
+ return self.metadata['Name']
505
+
506
+ @property
507
+ def _normalized_name(self):
508
+ """Return a normalized version of the name."""
509
+ return Prepared.normalize(self.name)
510
+
511
+ @property
512
+ def version(self) -> str:
513
+ """Return the 'Version' metadata for the distribution package."""
514
+ return self.metadata['Version']
515
+
516
+ @property
517
+ def entry_points(self) -> EntryPoints:
518
+ """
519
+ Return EntryPoints for this distribution.
520
+
521
+ Custom providers may provide the ``entry_points.txt`` file
522
+ or override this property.
523
+ """
524
+ return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
525
+
526
+ @property
527
+ def files(self) -> Optional[List[PackagePath]]:
528
+ """Files in this distribution.
529
+
530
+ :return: List of PackagePath for this distribution or None
531
+
532
+ Result is `None` if the metadata file that enumerates files
533
+ (i.e. RECORD for dist-info, or installed-files.txt or
534
+ SOURCES.txt for egg-info) is missing.
535
+ Result may be empty if the metadata exists but is empty.
536
+
537
+ Custom providers are recommended to provide a "RECORD" file (in
538
+ ``read_text``) or override this property to allow for callers to be
539
+ able to resolve filenames provided by the package.
540
+ """
541
+
542
+ def make_file(name, hash=None, size_str=None):
543
+ result = PackagePath(name)
544
+ result.hash = FileHash(hash) if hash else None
545
+ result.size = int(size_str) if size_str else None
546
+ result.dist = self
547
+ return result
548
+
549
+ @pass_none
550
+ def make_files(lines):
551
+ # Delay csv import, since Distribution.files is not as widely used
552
+ # as other parts of importlib.metadata
553
+ import csv
554
+
555
+ return starmap(make_file, csv.reader(lines))
556
+
557
+ @pass_none
558
+ def skip_missing_files(package_paths):
559
+ return list(filter(lambda path: path.locate().exists(), package_paths))
560
+
561
+ return skip_missing_files(
562
+ make_files(
563
+ self._read_files_distinfo()
564
+ or self._read_files_egginfo_installed()
565
+ or self._read_files_egginfo_sources()
566
+ )
567
+ )
568
+
569
+ def _read_files_distinfo(self):
570
+ """
571
+ Read the lines of RECORD.
572
+ """
573
+ text = self.read_text('RECORD')
574
+ return text and text.splitlines()
575
+
576
+ def _read_files_egginfo_installed(self):
577
+ """
578
+ Read installed-files.txt and return lines in a similar
579
+ CSV-parsable format as RECORD: each file must be placed
580
+ relative to the site-packages directory and must also be
581
+ quoted (since file names can contain literal commas).
582
+
583
+ This file is written when the package is installed by pip,
584
+ but it might not be written for other installation methods.
585
+ Assume the file is accurate if it exists.
586
+ """
587
+ text = self.read_text('installed-files.txt')
588
+ # Prepend the .egg-info/ subdir to the lines in this file.
589
+ # But this subdir is only available from PathDistribution's
590
+ # self._path.
591
+ subdir = getattr(self, '_path', None)
592
+ if not text or not subdir:
593
+ return
594
+
595
+ paths = (
596
+ py311.relative_fix((subdir / name).resolve())
597
+ .relative_to(self.locate_file('').resolve(), walk_up=True)
598
+ .as_posix()
599
+ for name in text.splitlines()
600
+ )
601
+ return map('"{}"'.format, paths)
602
+
603
+ def _read_files_egginfo_sources(self):
604
+ """
605
+ Read SOURCES.txt and return lines in a similar CSV-parsable
606
+ format as RECORD: each file name must be quoted (since it
607
+ might contain literal commas).
608
+
609
+ Note that SOURCES.txt is not a reliable source for what
610
+ files are installed by a package. This file is generated
611
+ for a source archive, and the files that are present
612
+ there (e.g. setup.py) may not correctly reflect the files
613
+ that are present after the package has been installed.
614
+ """
615
+ text = self.read_text('SOURCES.txt')
616
+ return text and map('"{}"'.format, text.splitlines())
617
+
618
+ @property
619
+ def requires(self) -> Optional[List[str]]:
620
+ """Generated requirements specified for this Distribution"""
621
+ reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
622
+ return reqs and list(reqs)
623
+
624
+ def _read_dist_info_reqs(self):
625
+ return self.metadata.get_all('Requires-Dist')
626
+
627
+ def _read_egg_info_reqs(self):
628
+ source = self.read_text('requires.txt')
629
+ return pass_none(self._deps_from_requires_text)(source)
630
+
631
+ @classmethod
632
+ def _deps_from_requires_text(cls, source):
633
+ return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
634
+
635
+ @staticmethod
636
+ def _convert_egg_info_reqs_to_simple_reqs(sections):
637
+ """
638
+ Historically, setuptools would solicit and store 'extra'
639
+ requirements, including those with environment markers,
640
+ in separate sections. More modern tools expect each
641
+ dependency to be defined separately, with any relevant
642
+ extras and environment markers attached directly to that
643
+ requirement. This method converts the former to the
644
+ latter. See _test_deps_from_requires_text for an example.
645
+ """
646
+
647
+ def make_condition(name):
648
+ return name and f'extra == "{name}"'
649
+
650
+ def quoted_marker(section):
651
+ section = section or ''
652
+ extra, sep, markers = section.partition(':')
653
+ if extra and markers:
654
+ markers = f'({markers})'
655
+ conditions = list(filter(None, [markers, make_condition(extra)]))
656
+ return '; ' + ' and '.join(conditions) if conditions else ''
657
+
658
+ def url_req_space(req):
659
+ """
660
+ PEP 508 requires a space between the url_spec and the quoted_marker.
661
+ Ref python/importlib_metadata#357.
662
+ """
663
+ # '@' is uniquely indicative of a url_req.
664
+ return ' ' * ('@' in req)
665
+
666
+ for section in sections:
667
+ space = url_req_space(section.value)
668
+ yield section.value + space + quoted_marker(section.name)
669
+
670
+ @property
671
+ def origin(self):
672
+ return self._load_json('direct_url.json')
673
+
674
+ def _load_json(self, filename):
675
+ # Deferred for performance (python/importlib_metadata#503)
676
+ import json
677
+
678
+ return pass_none(json.loads)(
679
+ self.read_text(filename),
680
+ object_hook=lambda data: types.SimpleNamespace(**data),
681
+ )
682
+
683
+
684
+ class DistributionFinder(MetaPathFinder):
685
+ """
686
+ A MetaPathFinder capable of discovering installed distributions.
687
+
688
+ Custom providers should implement this interface in order to
689
+ supply metadata.
690
+ """
691
+
692
+ class Context:
693
+ """
694
+ Keyword arguments presented by the caller to
695
+ ``distributions()`` or ``Distribution.discover()``
696
+ to narrow the scope of a search for distributions
697
+ in all DistributionFinders.
698
+
699
+ Each DistributionFinder may expect any parameters
700
+ and should attempt to honor the canonical
701
+ parameters defined below when appropriate.
702
+
703
+ This mechanism gives a custom provider a means to
704
+ solicit additional details from the caller beyond
705
+ "name" and "path" when searching distributions.
706
+ For example, imagine a provider that exposes suites
707
+ of packages in either a "public" or "private" ``realm``.
708
+ A caller may wish to query only for distributions in
709
+ a particular realm and could call
710
+ ``distributions(realm="private")`` to signal to the
711
+ custom provider to only include distributions from that
712
+ realm.
713
+ """
714
+
715
+ name = None
716
+ """
717
+ Specific name for which a distribution finder should match.
718
+ A name of ``None`` matches all distributions.
719
+ """
720
+
721
+ def __init__(self, **kwargs):
722
+ vars(self).update(kwargs)
723
+
724
+ @property
725
+ def path(self) -> List[str]:
726
+ """
727
+ The sequence of directory path that a distribution finder
728
+ should search.
729
+
730
+ Typically refers to Python installed package paths such as
731
+ "site-packages" directories and defaults to ``sys.path``.
732
+ """
733
+ return vars(self).get('path', sys.path)
734
+
735
+ @abc.abstractmethod
736
+ def find_distributions(self, context=Context()) -> Iterable[Distribution]:
737
+ """
738
+ Find distributions.
739
+
740
+ Return an iterable of all Distribution instances capable of
741
+ loading the metadata for packages matching the ``context``,
742
+ a DistributionFinder.Context instance.
743
+ """
744
+
745
+
746
+ class FastPath:
747
+ """
748
+ Micro-optimized class for searching a root for children.
749
+
750
+ Root is a path on the file system that may contain metadata
751
+ directories either as natural directories or within a zip file.
752
+
753
+ >>> FastPath('').children()
754
+ ['...']
755
+
756
+ FastPath objects are cached and recycled for any given root.
757
+
758
+ >>> FastPath('foobar') is FastPath('foobar')
759
+ True
760
+ """
761
+
762
+ @functools.lru_cache() # type: ignore[misc]
763
+ def __new__(cls, root):
764
+ return super().__new__(cls)
765
+
766
+ def __init__(self, root):
767
+ self.root = root
768
+
769
+ def joinpath(self, child):
770
+ return pathlib.Path(self.root, child)
771
+
772
+ def children(self):
773
+ with suppress(Exception):
774
+ return os.listdir(self.root or '.')
775
+ with suppress(Exception):
776
+ return self.zip_children()
777
+ return []
778
+
779
+ def zip_children(self):
780
+ # deferred for performance (python/importlib_metadata#502)
781
+ from zipp.compat.overlay import zipfile
782
+
783
+ zip_path = zipfile.Path(self.root)
784
+ names = zip_path.root.namelist()
785
+ self.joinpath = zip_path.joinpath
786
+
787
+ return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
788
+
789
+ def search(self, name):
790
+ return self.lookup(self.mtime).search(name)
791
+
792
+ @property
793
+ def mtime(self):
794
+ with suppress(OSError):
795
+ return os.stat(self.root).st_mtime
796
+ self.lookup.cache_clear()
797
+
798
+ @method_cache
799
+ def lookup(self, mtime):
800
+ return Lookup(self)
801
+
802
+
803
+ class Lookup:
804
+ """
805
+ A micro-optimized class for searching a (fast) path for metadata.
806
+ """
807
+
808
+ def __init__(self, path: FastPath):
809
+ """
810
+ Calculate all of the children representing metadata.
811
+
812
+ From the children in the path, calculate early all of the
813
+ children that appear to represent metadata (infos) or legacy
814
+ metadata (eggs).
815
+ """
816
+
817
+ base = os.path.basename(path.root).lower()
818
+ base_is_egg = base.endswith(".egg")
819
+ self.infos = FreezableDefaultDict(list)
820
+ self.eggs = FreezableDefaultDict(list)
821
+
822
+ for child in path.children():
823
+ low = child.lower()
824
+ if low.endswith((".dist-info", ".egg-info")):
825
+ # rpartition is faster than splitext and suitable for this purpose.
826
+ name = low.rpartition(".")[0].partition("-")[0]
827
+ normalized = Prepared.normalize(name)
828
+ self.infos[normalized].append(path.joinpath(child))
829
+ elif base_is_egg and low == "egg-info":
830
+ name = base.rpartition(".")[0].partition("-")[0]
831
+ legacy_normalized = Prepared.legacy_normalize(name)
832
+ self.eggs[legacy_normalized].append(path.joinpath(child))
833
+
834
+ self.infos.freeze()
835
+ self.eggs.freeze()
836
+
837
+ def search(self, prepared: Prepared):
838
+ """
839
+ Yield all infos and eggs matching the Prepared query.
840
+ """
841
+ infos = (
842
+ self.infos[prepared.normalized]
843
+ if prepared
844
+ else itertools.chain.from_iterable(self.infos.values())
845
+ )
846
+ eggs = (
847
+ self.eggs[prepared.legacy_normalized]
848
+ if prepared
849
+ else itertools.chain.from_iterable(self.eggs.values())
850
+ )
851
+ return itertools.chain(infos, eggs)
852
+
853
+
854
+ class Prepared:
855
+ """
856
+ A prepared search query for metadata on a possibly-named package.
857
+
858
+ Pre-calculates the normalization to prevent repeated operations.
859
+
860
+ >>> none = Prepared(None)
861
+ >>> none.normalized
862
+ >>> none.legacy_normalized
863
+ >>> bool(none)
864
+ False
865
+ >>> sample = Prepared('Sample__Pkg-name.foo')
866
+ >>> sample.normalized
867
+ 'sample_pkg_name_foo'
868
+ >>> sample.legacy_normalized
869
+ 'sample__pkg_name.foo'
870
+ >>> bool(sample)
871
+ True
872
+ """
873
+
874
+ normalized = None
875
+ legacy_normalized = None
876
+
877
+ def __init__(self, name: Optional[str]):
878
+ self.name = name
879
+ if name is None:
880
+ return
881
+ self.normalized = self.normalize(name)
882
+ self.legacy_normalized = self.legacy_normalize(name)
883
+
884
+ @staticmethod
885
+ def normalize(name):
886
+ """
887
+ PEP 503 normalization plus dashes as underscores.
888
+ """
889
+ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
890
+
891
+ @staticmethod
892
+ def legacy_normalize(name):
893
+ """
894
+ Normalize the package name as found in the convention in
895
+ older packaging tools versions and specs.
896
+ """
897
+ return name.lower().replace('-', '_')
898
+
899
+ def __bool__(self):
900
+ return bool(self.name)
901
+
902
+
903
+ @install
904
+ class MetadataPathFinder(NullFinder, DistributionFinder):
905
+ """A degenerate finder for distribution packages on the file system.
906
+
907
+ This finder supplies only a find_distributions() method for versions
908
+ of Python that do not have a PathFinder find_distributions().
909
+ """
910
+
911
+ @classmethod
912
+ def find_distributions(
913
+ cls, context=DistributionFinder.Context()
914
+ ) -> Iterable[PathDistribution]:
915
+ """
916
+ Find distributions.
917
+
918
+ Return an iterable of all Distribution instances capable of
919
+ loading the metadata for packages matching ``context.name``
920
+ (or all names if ``None`` indicated) along the paths in the list
921
+ of directories ``context.path``.
922
+ """
923
+ found = cls._search_paths(context.name, context.path)
924
+ return map(PathDistribution, found)
925
+
926
+ @classmethod
927
+ def _search_paths(cls, name, paths):
928
+ """Find metadata directories in paths heuristically."""
929
+ prepared = Prepared(name)
930
+ return itertools.chain.from_iterable(
931
+ path.search(prepared) for path in map(FastPath, paths)
932
+ )
933
+
934
+ @classmethod
935
+ def invalidate_caches(cls) -> None:
936
+ FastPath.__new__.cache_clear()
937
+
938
+
939
+ class PathDistribution(Distribution):
940
+ def __init__(self, path: SimplePath) -> None:
941
+ """Construct a distribution.
942
+
943
+ :param path: SimplePath indicating the metadata directory.
944
+ """
945
+ self._path = path
946
+
947
+ def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
948
+ with suppress(
949
+ FileNotFoundError,
950
+ IsADirectoryError,
951
+ KeyError,
952
+ NotADirectoryError,
953
+ PermissionError,
954
+ ):
955
+ return self._path.joinpath(filename).read_text(encoding='utf-8')
956
+
957
+ return None
958
+
959
+ read_text.__doc__ = Distribution.read_text.__doc__
960
+
961
+ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
962
+ return self._path.parent / path
963
+
964
+ @property
965
+ def _normalized_name(self):
966
+ """
967
+ Performance optimization: where possible, resolve the
968
+ normalized name from the file system path.
969
+ """
970
+ stem = os.path.basename(str(self._path))
971
+ return (
972
+ pass_none(Prepared.normalize)(self._name_from_stem(stem))
973
+ or super()._normalized_name
974
+ )
975
+
976
+ @staticmethod
977
+ def _name_from_stem(stem):
978
+ """
979
+ >>> PathDistribution._name_from_stem('foo-3.0.egg-info')
980
+ 'foo'
981
+ >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info')
982
+ 'CherryPy'
983
+ >>> PathDistribution._name_from_stem('face.egg-info')
984
+ 'face'
985
+ >>> PathDistribution._name_from_stem('foo.bar')
986
+ """
987
+ filename, ext = os.path.splitext(stem)
988
+ if ext not in ('.dist-info', '.egg-info'):
989
+ return
990
+ name, sep, rest = filename.partition('-')
991
+ return name
992
+
993
+
994
+ def distribution(distribution_name: str) -> Distribution:
995
+ """Get the ``Distribution`` instance for the named package.
996
+
997
+ :param distribution_name: The name of the distribution package as a string.
998
+ :return: A ``Distribution`` instance (or subclass thereof).
999
+ """
1000
+ return Distribution.from_name(distribution_name)
1001
+
1002
+
1003
+ def distributions(**kwargs) -> Iterable[Distribution]:
1004
+ """Get all ``Distribution`` instances in the current environment.
1005
+
1006
+ :return: An iterable of ``Distribution`` instances.
1007
+ """
1008
+ return Distribution.discover(**kwargs)
1009
+
1010
+
1011
+ def metadata(distribution_name: str) -> _meta.PackageMetadata:
1012
+ """Get the metadata for the named package.
1013
+
1014
+ :param distribution_name: The name of the distribution package to query.
1015
+ :return: A PackageMetadata containing the parsed metadata.
1016
+ """
1017
+ return Distribution.from_name(distribution_name).metadata
1018
+
1019
+
1020
+ def version(distribution_name: str) -> str:
1021
+ """Get the version string for the named package.
1022
+
1023
+ :param distribution_name: The name of the distribution package to query.
1024
+ :return: The version string for the package as defined in the package's
1025
+ "Version" metadata key.
1026
+ """
1027
+ return distribution(distribution_name).version
1028
+
1029
+
1030
+ _unique = functools.partial(
1031
+ unique_everseen,
1032
+ key=py39.normalized_name,
1033
+ )
1034
+ """
1035
+ Wrapper for ``distributions`` to return unique distributions by name.
1036
+ """
1037
+
1038
+
1039
+ def entry_points(**params) -> EntryPoints:
1040
+ """Return EntryPoint objects for all installed packages.
1041
+
1042
+ Pass selection parameters (group or name) to filter the
1043
+ result to entry points matching those properties (see
1044
+ EntryPoints.select()).
1045
+
1046
+ :return: EntryPoints for all installed packages.
1047
+ """
1048
+ eps = itertools.chain.from_iterable(
1049
+ dist.entry_points for dist in _unique(distributions())
1050
+ )
1051
+ return EntryPoints(eps).select(**params)
1052
+
1053
+
1054
+ def files(distribution_name: str) -> Optional[List[PackagePath]]:
1055
+ """Return a list of files for the named package.
1056
+
1057
+ :param distribution_name: The name of the distribution package to query.
1058
+ :return: List of files composing the distribution.
1059
+ """
1060
+ return distribution(distribution_name).files
1061
+
1062
+
1063
+ def requires(distribution_name: str) -> Optional[List[str]]:
1064
+ """
1065
+ Return a list of requirements for the named package.
1066
+
1067
+ :return: An iterable of requirements, suitable for
1068
+ packaging.requirement.Requirement.
1069
+ """
1070
+ return distribution(distribution_name).requires
1071
+
1072
+
1073
+ def packages_distributions() -> Mapping[str, List[str]]:
1074
+ """
1075
+ Return a mapping of top-level packages to their
1076
+ distributions.
1077
+
1078
+ >>> import collections.abc
1079
+ >>> pkgs = packages_distributions()
1080
+ >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values())
1081
+ True
1082
+ """
1083
+ pkg_to_dist = collections.defaultdict(list)
1084
+ for dist in distributions():
1085
+ for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
1086
+ pkg_to_dist[pkg].append(dist.metadata['Name'])
1087
+ return dict(pkg_to_dist)
1088
+
1089
+
1090
+ def _top_level_declared(dist):
1091
+ return (dist.read_text('top_level.txt') or '').split()
1092
+
1093
+
1094
+ def _topmost(name: PackagePath) -> Optional[str]:
1095
+ """
1096
+ Return the top-most parent as long as there is a parent.
1097
+ """
1098
+ top, *rest = name.parts
1099
+ return top if rest else None
1100
+
1101
+
1102
+ def _get_toplevel_name(name: PackagePath) -> str:
1103
+ """
1104
+ Infer a possibly importable module name from a name presumed on
1105
+ sys.path.
1106
+
1107
+ >>> _get_toplevel_name(PackagePath('foo.py'))
1108
+ 'foo'
1109
+ >>> _get_toplevel_name(PackagePath('foo'))
1110
+ 'foo'
1111
+ >>> _get_toplevel_name(PackagePath('foo.pyc'))
1112
+ 'foo'
1113
+ >>> _get_toplevel_name(PackagePath('foo/__init__.py'))
1114
+ 'foo'
1115
+ >>> _get_toplevel_name(PackagePath('foo.pth'))
1116
+ 'foo.pth'
1117
+ >>> _get_toplevel_name(PackagePath('foo.dist-info'))
1118
+ 'foo.dist-info'
1119
+ """
1120
+ # Defer import of inspect for performance (python/cpython#118761)
1121
+ import inspect
1122
+
1123
+ return _topmost(name) or inspect.getmodulename(name) or str(name)
1124
+
1125
+
1126
+ def _top_level_inferred(dist):
1127
+ opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))
1128
+
1129
+ def importable_name(name):
1130
+ return '.' not in name
1131
+
1132
+ return filter(importable_name, opt_names)
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (61.2 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_adapters.cpython-311.pyc ADDED
Binary file (6.78 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_collections.cpython-311.pyc ADDED
Binary file (2.17 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_compat.cpython-311.pyc ADDED
Binary file (2.46 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_functools.cpython-311.pyc ADDED
Binary file (3.61 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_itertools.cpython-311.pyc ADDED
Binary file (7 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/_meta.cpython-311.pyc ADDED
Binary file (4.11 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/__pycache__/diagnose.cpython-311.pyc ADDED
Binary file (1.37 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/_adapters.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import email.message
2
+ import email.policy
3
+ import re
4
+ import textwrap
5
+
6
+ from ._text import FoldedCase
7
+
8
+
9
+ class RawPolicy(email.policy.EmailPolicy):
10
+ def fold(self, name, value):
11
+ folded = self.linesep.join(
12
+ textwrap.indent(value, prefix=' ' * 8, predicate=lambda line: True)
13
+ .lstrip()
14
+ .splitlines()
15
+ )
16
+ return f'{name}: {folded}{self.linesep}'
17
+
18
+
19
+ class Message(email.message.Message):
20
+ r"""
21
+ Specialized Message subclass to handle metadata naturally.
22
+
23
+ Reads values that may have newlines in them and converts the
24
+ payload to the Description.
25
+
26
+ >>> msg_text = textwrap.dedent('''
27
+ ... Name: Foo
28
+ ... Version: 3.0
29
+ ... License: blah
30
+ ... de-blah
31
+ ... <BLANKLINE>
32
+ ... First line of description.
33
+ ... Second line of description.
34
+ ... <BLANKLINE>
35
+ ... Fourth line!
36
+ ... ''').lstrip().replace('<BLANKLINE>', '')
37
+ >>> msg = Message(email.message_from_string(msg_text))
38
+ >>> msg['Description']
39
+ 'First line of description.\nSecond line of description.\n\nFourth line!\n'
40
+
41
+ Message should render even if values contain newlines.
42
+
43
+ >>> print(msg)
44
+ Name: Foo
45
+ Version: 3.0
46
+ License: blah
47
+ de-blah
48
+ Description: First line of description.
49
+ Second line of description.
50
+ <BLANKLINE>
51
+ Fourth line!
52
+ <BLANKLINE>
53
+ <BLANKLINE>
54
+ """
55
+
56
+ multiple_use_keys = set(
57
+ map(
58
+ FoldedCase,
59
+ [
60
+ 'Classifier',
61
+ 'Obsoletes-Dist',
62
+ 'Platform',
63
+ 'Project-URL',
64
+ 'Provides-Dist',
65
+ 'Provides-Extra',
66
+ 'Requires-Dist',
67
+ 'Requires-External',
68
+ 'Supported-Platform',
69
+ 'Dynamic',
70
+ ],
71
+ )
72
+ )
73
+ """
74
+ Keys that may be indicated multiple times per PEP 566.
75
+ """
76
+
77
+ def __new__(cls, orig: email.message.Message):
78
+ res = super().__new__(cls)
79
+ vars(res).update(vars(orig))
80
+ return res
81
+
82
+ def __init__(self, *args, **kwargs):
83
+ self._headers = self._repair_headers()
84
+
85
+ # suppress spurious error from mypy
86
+ def __iter__(self):
87
+ return super().__iter__()
88
+
89
+ def __getitem__(self, item):
90
+ """
91
+ Override parent behavior to typical dict behavior.
92
+
93
+ ``email.message.Message`` will emit None values for missing
94
+ keys. Typical mappings, including this ``Message``, will raise
95
+ a key error for missing keys.
96
+
97
+ Ref python/importlib_metadata#371.
98
+ """
99
+ res = super().__getitem__(item)
100
+ if res is None:
101
+ raise KeyError(item)
102
+ return res
103
+
104
+ def _repair_headers(self):
105
+ def redent(value):
106
+ "Correct for RFC822 indentation"
107
+ indent = ' ' * 8
108
+ if not value or '\n' + indent not in value:
109
+ return value
110
+ return textwrap.dedent(indent + value)
111
+
112
+ headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
113
+ if self._payload:
114
+ headers.append(('Description', self.get_payload()))
115
+ self.set_payload('')
116
+ return headers
117
+
118
+ def as_string(self):
119
+ return super().as_string(policy=RawPolicy())
120
+
121
+ @property
122
+ def json(self):
123
+ """
124
+ Convert PackageMetadata to a JSON-compatible format
125
+ per PEP 0566.
126
+ """
127
+
128
+ def transform(key):
129
+ value = self.get_all(key) if key in self.multiple_use_keys else self[key]
130
+ if key == 'Keywords':
131
+ value = re.split(r'\s+', value)
132
+ tk = key.lower().replace('-', '_')
133
+ return tk, value
134
+
135
+ return dict(map(transform, map(FoldedCase, self)))
.venv/lib/python3.11/site-packages/importlib_metadata/_collections.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import collections
2
+
3
+
4
+ # from jaraco.collections 3.3
5
+ class FreezableDefaultDict(collections.defaultdict):
6
+ """
7
+ Often it is desirable to prevent the mutation of
8
+ a default dict after its initial construction, such
9
+ as to prevent mutation during iteration.
10
+
11
+ >>> dd = FreezableDefaultDict(list)
12
+ >>> dd[0].append('1')
13
+ >>> dd.freeze()
14
+ >>> dd[1]
15
+ []
16
+ >>> len(dd)
17
+ 1
18
+ """
19
+
20
+ def __missing__(self, key):
21
+ return getattr(self, '_frozen', super().__missing__)(key)
22
+
23
+ def freeze(self):
24
+ self._frozen = lambda key: self.default_factory()
25
+
26
+
27
+ class Pair(collections.namedtuple('Pair', 'name value')):
28
+ @classmethod
29
+ def parse(cls, text):
30
+ return cls(*map(str.strip, text.split("=", 1)))
.venv/lib/python3.11/site-packages/importlib_metadata/_compat.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import platform
2
+ import sys
3
+
4
+ __all__ = ['install', 'NullFinder']
5
+
6
+
7
+ def install(cls):
8
+ """
9
+ Class decorator for installation on sys.meta_path.
10
+
11
+ Adds the backport DistributionFinder to sys.meta_path and
12
+ attempts to disable the finder functionality of the stdlib
13
+ DistributionFinder.
14
+ """
15
+ sys.meta_path.append(cls())
16
+ disable_stdlib_finder()
17
+ return cls
18
+
19
+
20
+ def disable_stdlib_finder():
21
+ """
22
+ Give the backport primacy for discovering path-based distributions
23
+ by monkey-patching the stdlib O_O.
24
+
25
+ See #91 for more background for rationale on this sketchy
26
+ behavior.
27
+ """
28
+
29
+ def matches(finder):
30
+ return getattr(
31
+ finder, '__module__', None
32
+ ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions')
33
+
34
+ for finder in filter(matches, sys.meta_path): # pragma: nocover
35
+ del finder.find_distributions
36
+
37
+
38
+ class NullFinder:
39
+ """
40
+ A "Finder" (aka "MetaPathFinder") that never finds any modules,
41
+ but may find distributions.
42
+ """
43
+
44
+ @staticmethod
45
+ def find_spec(*args, **kwargs):
46
+ return None
47
+
48
+
49
+ def pypy_partial(val):
50
+ """
51
+ Adjust for variable stacklevel on partial under PyPy.
52
+
53
+ Workaround for #327.
54
+ """
55
+ is_pypy = platform.python_implementation() == 'PyPy'
56
+ return val + is_pypy
.venv/lib/python3.11/site-packages/importlib_metadata/_functools.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import types
3
+
4
+
5
+ # from jaraco.functools 3.3
6
+ def method_cache(method, cache_wrapper=None):
7
+ """
8
+ Wrap lru_cache to support storing the cache data in the object instances.
9
+
10
+ Abstracts the common paradigm where the method explicitly saves an
11
+ underscore-prefixed protected property on first call and returns that
12
+ subsequently.
13
+
14
+ >>> class MyClass:
15
+ ... calls = 0
16
+ ...
17
+ ... @method_cache
18
+ ... def method(self, value):
19
+ ... self.calls += 1
20
+ ... return value
21
+
22
+ >>> a = MyClass()
23
+ >>> a.method(3)
24
+ 3
25
+ >>> for x in range(75):
26
+ ... res = a.method(x)
27
+ >>> a.calls
28
+ 75
29
+
30
+ Note that the apparent behavior will be exactly like that of lru_cache
31
+ except that the cache is stored on each instance, so values in one
32
+ instance will not flush values from another, and when an instance is
33
+ deleted, so are the cached values for that instance.
34
+
35
+ >>> b = MyClass()
36
+ >>> for x in range(35):
37
+ ... res = b.method(x)
38
+ >>> b.calls
39
+ 35
40
+ >>> a.method(0)
41
+ 0
42
+ >>> a.calls
43
+ 75
44
+
45
+ Note that if method had been decorated with ``functools.lru_cache()``,
46
+ a.calls would have been 76 (due to the cached value of 0 having been
47
+ flushed by the 'b' instance).
48
+
49
+ Clear the cache with ``.cache_clear()``
50
+
51
+ >>> a.method.cache_clear()
52
+
53
+ Same for a method that hasn't yet been called.
54
+
55
+ >>> c = MyClass()
56
+ >>> c.method.cache_clear()
57
+
58
+ Another cache wrapper may be supplied:
59
+
60
+ >>> cache = functools.lru_cache(maxsize=2)
61
+ >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
62
+ >>> a = MyClass()
63
+ >>> a.method2()
64
+ 3
65
+
66
+ Caution - do not subsequently wrap the method with another decorator, such
67
+ as ``@property``, which changes the semantics of the function.
68
+
69
+ See also
70
+ http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
71
+ for another implementation and additional justification.
72
+ """
73
+ cache_wrapper = cache_wrapper or functools.lru_cache()
74
+
75
+ def wrapper(self, *args, **kwargs):
76
+ # it's the first call, replace the method with a cached, bound method
77
+ bound_method = types.MethodType(method, self)
78
+ cached_method = cache_wrapper(bound_method)
79
+ setattr(self, method.__name__, cached_method)
80
+ return cached_method(*args, **kwargs)
81
+
82
+ # Support cache clear even before cache has been created.
83
+ wrapper.cache_clear = lambda: None
84
+
85
+ return wrapper
86
+
87
+
88
+ # From jaraco.functools 3.3
89
+ def pass_none(func):
90
+ """
91
+ Wrap func so it's not called if its first param is None
92
+
93
+ >>> print_text = pass_none(print)
94
+ >>> print_text('text')
95
+ text
96
+ >>> print_text(None)
97
+ """
98
+
99
+ @functools.wraps(func)
100
+ def wrapper(param, *args, **kwargs):
101
+ if param is not None:
102
+ return func(param, *args, **kwargs)
103
+
104
+ return wrapper
.venv/lib/python3.11/site-packages/importlib_metadata/_itertools.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict, deque
2
+ from itertools import filterfalse
3
+
4
+
5
+ def unique_everseen(iterable, key=None):
6
+ "List unique elements, preserving order. Remember all elements ever seen."
7
+ # unique_everseen('AAAABBBCCDAABBB') --> A B C D
8
+ # unique_everseen('ABBCcAD', str.lower) --> A B C D
9
+ seen = set()
10
+ seen_add = seen.add
11
+ if key is None:
12
+ for element in filterfalse(seen.__contains__, iterable):
13
+ seen_add(element)
14
+ yield element
15
+ else:
16
+ for element in iterable:
17
+ k = key(element)
18
+ if k not in seen:
19
+ seen_add(k)
20
+ yield element
21
+
22
+
23
+ # copied from more_itertools 8.8
24
+ def always_iterable(obj, base_type=(str, bytes)):
25
+ """If *obj* is iterable, return an iterator over its items::
26
+
27
+ >>> obj = (1, 2, 3)
28
+ >>> list(always_iterable(obj))
29
+ [1, 2, 3]
30
+
31
+ If *obj* is not iterable, return a one-item iterable containing *obj*::
32
+
33
+ >>> obj = 1
34
+ >>> list(always_iterable(obj))
35
+ [1]
36
+
37
+ If *obj* is ``None``, return an empty iterable:
38
+
39
+ >>> obj = None
40
+ >>> list(always_iterable(None))
41
+ []
42
+
43
+ By default, binary and text strings are not considered iterable::
44
+
45
+ >>> obj = 'foo'
46
+ >>> list(always_iterable(obj))
47
+ ['foo']
48
+
49
+ If *base_type* is set, objects for which ``isinstance(obj, base_type)``
50
+ returns ``True`` won't be considered iterable.
51
+
52
+ >>> obj = {'a': 1}
53
+ >>> list(always_iterable(obj)) # Iterate over the dict's keys
54
+ ['a']
55
+ >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
56
+ [{'a': 1}]
57
+
58
+ Set *base_type* to ``None`` to avoid any special handling and treat objects
59
+ Python considers iterable as iterable:
60
+
61
+ >>> obj = 'foo'
62
+ >>> list(always_iterable(obj, base_type=None))
63
+ ['f', 'o', 'o']
64
+ """
65
+ if obj is None:
66
+ return iter(())
67
+
68
+ if (base_type is not None) and isinstance(obj, base_type):
69
+ return iter((obj,))
70
+
71
+ try:
72
+ return iter(obj)
73
+ except TypeError:
74
+ return iter((obj,))
75
+
76
+
77
+ # Copied from more_itertools 10.3
78
+ class bucket:
79
+ """Wrap *iterable* and return an object that buckets the iterable into
80
+ child iterables based on a *key* function.
81
+
82
+ >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3']
83
+ >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character
84
+ >>> sorted(list(s)) # Get the keys
85
+ ['a', 'b', 'c']
86
+ >>> a_iterable = s['a']
87
+ >>> next(a_iterable)
88
+ 'a1'
89
+ >>> next(a_iterable)
90
+ 'a2'
91
+ >>> list(s['b'])
92
+ ['b1', 'b2', 'b3']
93
+
94
+ The original iterable will be advanced and its items will be cached until
95
+ they are used by the child iterables. This may require significant storage.
96
+
97
+ By default, attempting to select a bucket to which no items belong will
98
+ exhaust the iterable and cache all values.
99
+ If you specify a *validator* function, selected buckets will instead be
100
+ checked against it.
101
+
102
+ >>> from itertools import count
103
+ >>> it = count(1, 2) # Infinite sequence of odd numbers
104
+ >>> key = lambda x: x % 10 # Bucket by last digit
105
+ >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only
106
+ >>> s = bucket(it, key=key, validator=validator)
107
+ >>> 2 in s
108
+ False
109
+ >>> list(s[2])
110
+ []
111
+
112
+ """
113
+
114
+ def __init__(self, iterable, key, validator=None):
115
+ self._it = iter(iterable)
116
+ self._key = key
117
+ self._cache = defaultdict(deque)
118
+ self._validator = validator or (lambda x: True)
119
+
120
+ def __contains__(self, value):
121
+ if not self._validator(value):
122
+ return False
123
+
124
+ try:
125
+ item = next(self[value])
126
+ except StopIteration:
127
+ return False
128
+ else:
129
+ self._cache[value].appendleft(item)
130
+
131
+ return True
132
+
133
+ def _get_values(self, value):
134
+ """
135
+ Helper to yield items from the parent iterator that match *value*.
136
+ Items that don't match are stored in the local cache as they
137
+ are encountered.
138
+ """
139
+ while True:
140
+ # If we've cached some items that match the target value, emit
141
+ # the first one and evict it from the cache.
142
+ if self._cache[value]:
143
+ yield self._cache[value].popleft()
144
+ # Otherwise we need to advance the parent iterator to search for
145
+ # a matching item, caching the rest.
146
+ else:
147
+ while True:
148
+ try:
149
+ item = next(self._it)
150
+ except StopIteration:
151
+ return
152
+ item_value = self._key(item)
153
+ if item_value == value:
154
+ yield item
155
+ break
156
+ elif self._validator(item_value):
157
+ self._cache[item_value].append(item)
158
+
159
+ def __iter__(self):
160
+ for item in self._it:
161
+ item_value = self._key(item)
162
+ if self._validator(item_value):
163
+ self._cache[item_value].append(item)
164
+
165
+ yield from self._cache.keys()
166
+
167
+ def __getitem__(self, value):
168
+ if not self._validator(value):
169
+ return iter(())
170
+
171
+ return self._get_values(value)
.venv/lib/python3.11/site-packages/importlib_metadata/_meta.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import (
5
+ Any,
6
+ Dict,
7
+ Iterator,
8
+ List,
9
+ Optional,
10
+ Protocol,
11
+ TypeVar,
12
+ Union,
13
+ overload,
14
+ )
15
+
16
+ _T = TypeVar("_T")
17
+
18
+
19
+ class PackageMetadata(Protocol):
20
+ def __len__(self) -> int: ... # pragma: no cover
21
+
22
+ def __contains__(self, item: str) -> bool: ... # pragma: no cover
23
+
24
+ def __getitem__(self, key: str) -> str: ... # pragma: no cover
25
+
26
+ def __iter__(self) -> Iterator[str]: ... # pragma: no cover
27
+
28
+ @overload
29
+ def get(
30
+ self, name: str, failobj: None = None
31
+ ) -> Optional[str]: ... # pragma: no cover
32
+
33
+ @overload
34
+ def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover
35
+
36
+ # overload per python/importlib_metadata#435
37
+ @overload
38
+ def get_all(
39
+ self, name: str, failobj: None = None
40
+ ) -> Optional[List[Any]]: ... # pragma: no cover
41
+
42
+ @overload
43
+ def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
44
+ """
45
+ Return all values associated with a possibly multi-valued key.
46
+ """
47
+
48
+ @property
49
+ def json(self) -> Dict[str, Union[str, List[str]]]:
50
+ """
51
+ A JSON-compatible form of the metadata.
52
+ """
53
+
54
+
55
+ class SimplePath(Protocol):
56
+ """
57
+ A minimal subset of pathlib.Path required by Distribution.
58
+ """
59
+
60
+ def joinpath(
61
+ self, other: Union[str, os.PathLike[str]]
62
+ ) -> SimplePath: ... # pragma: no cover
63
+
64
+ def __truediv__(
65
+ self, other: Union[str, os.PathLike[str]]
66
+ ) -> SimplePath: ... # pragma: no cover
67
+
68
+ @property
69
+ def parent(self) -> SimplePath: ... # pragma: no cover
70
+
71
+ def read_text(self, encoding=None) -> str: ... # pragma: no cover
72
+
73
+ def read_bytes(self) -> bytes: ... # pragma: no cover
74
+
75
+ def exists(self) -> bool: ... # pragma: no cover
.venv/lib/python3.11/site-packages/importlib_metadata/_text.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ from ._functools import method_cache
4
+
5
+
6
+ # from jaraco.text 3.5
7
+ class FoldedCase(str):
8
+ """
9
+ A case insensitive string class; behaves just like str
10
+ except compares equal when the only variation is case.
11
+
12
+ >>> s = FoldedCase('hello world')
13
+
14
+ >>> s == 'Hello World'
15
+ True
16
+
17
+ >>> 'Hello World' == s
18
+ True
19
+
20
+ >>> s != 'Hello World'
21
+ False
22
+
23
+ >>> s.index('O')
24
+ 4
25
+
26
+ >>> s.split('O')
27
+ ['hell', ' w', 'rld']
28
+
29
+ >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
30
+ ['alpha', 'Beta', 'GAMMA']
31
+
32
+ Sequence membership is straightforward.
33
+
34
+ >>> "Hello World" in [s]
35
+ True
36
+ >>> s in ["Hello World"]
37
+ True
38
+
39
+ You may test for set inclusion, but candidate and elements
40
+ must both be folded.
41
+
42
+ >>> FoldedCase("Hello World") in {s}
43
+ True
44
+ >>> s in {FoldedCase("Hello World")}
45
+ True
46
+
47
+ String inclusion works as long as the FoldedCase object
48
+ is on the right.
49
+
50
+ >>> "hello" in FoldedCase("Hello World")
51
+ True
52
+
53
+ But not if the FoldedCase object is on the left:
54
+
55
+ >>> FoldedCase('hello') in 'Hello World'
56
+ False
57
+
58
+ In that case, use in_:
59
+
60
+ >>> FoldedCase('hello').in_('Hello World')
61
+ True
62
+
63
+ >>> FoldedCase('hello') > FoldedCase('Hello')
64
+ False
65
+ """
66
+
67
+ def __lt__(self, other):
68
+ return self.lower() < other.lower()
69
+
70
+ def __gt__(self, other):
71
+ return self.lower() > other.lower()
72
+
73
+ def __eq__(self, other):
74
+ return self.lower() == other.lower()
75
+
76
+ def __ne__(self, other):
77
+ return self.lower() != other.lower()
78
+
79
+ def __hash__(self):
80
+ return hash(self.lower())
81
+
82
+ def __contains__(self, other):
83
+ return super().lower().__contains__(other.lower())
84
+
85
+ def in_(self, other):
86
+ "Does self appear in other?"
87
+ return self in FoldedCase(other)
88
+
89
+ # cache lower since it's likely to be called frequently.
90
+ @method_cache
91
+ def lower(self):
92
+ return super().lower()
93
+
94
+ def index(self, sub):
95
+ return self.lower().index(sub.lower())
96
+
97
+ def split(self, splitter=' ', maxsplit=0):
98
+ pattern = re.compile(re.escape(splitter), re.I)
99
+ return pattern.split(self, maxsplit)
.venv/lib/python3.11/site-packages/importlib_metadata/compat/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/importlib_metadata/compat/__pycache__/py39.cpython-311.pyc ADDED
Binary file (1.78 kB). View file
 
.venv/lib/python3.11/site-packages/importlib_metadata/compat/py311.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pathlib
3
+ import sys
4
+ import types
5
+
6
+
7
+ def wrap(path): # pragma: no cover
8
+ """
9
+ Workaround for https://github.com/python/cpython/issues/84538
10
+ to add backward compatibility for walk_up=True.
11
+ An example affected package is dask-labextension, which uses
12
+ jupyter-packaging to install JupyterLab javascript files outside
13
+ of site-packages.
14
+ """
15
+
16
+ def relative_to(root, *, walk_up=False):
17
+ return pathlib.Path(os.path.relpath(path, root))
18
+
19
+ return types.SimpleNamespace(relative_to=relative_to)
20
+
21
+
22
+ relative_fix = wrap if sys.version_info < (3, 12) else lambda x: x
.venv/lib/python3.11/site-packages/importlib_metadata/compat/py39.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Compatibility layer with Python 3.8/3.9
3
+ """
4
+
5
+ from typing import TYPE_CHECKING, Any, Optional
6
+
7
+ if TYPE_CHECKING: # pragma: no cover
8
+ # Prevent circular imports on runtime.
9
+ from .. import Distribution, EntryPoint
10
+ else:
11
+ Distribution = EntryPoint = Any
12
+
13
+
14
+ def normalized_name(dist: Distribution) -> Optional[str]:
15
+ """
16
+ Honor name normalization for distributions that don't provide ``_normalized_name``.
17
+ """
18
+ try:
19
+ return dist._normalized_name
20
+ except AttributeError:
21
+ from .. import Prepared # -> delay to prevent circular imports.
22
+
23
+ return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name'])
24
+
25
+
26
+ def ep_matches(ep: EntryPoint, **params) -> bool:
27
+ """
28
+ Workaround for ``EntryPoint`` objects without the ``matches`` method.
29
+ """
30
+ try:
31
+ return ep.matches(**params)
32
+ except AttributeError:
33
+ from .. import EntryPoint # -> delay to prevent circular imports.
34
+
35
+ # Reconstruct the EntryPoint object to make sure it is compatible.
36
+ return EntryPoint(ep.name, ep.value, ep.group).matches(**params)
.venv/lib/python3.11/site-packages/importlib_metadata/diagnose.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+
3
+ from . import Distribution
4
+
5
+
6
+ def inspect(path):
7
+ print("Inspecting", path)
8
+ dists = list(Distribution.discover(path=[path]))
9
+ if not dists:
10
+ return
11
+ print("Found", len(dists), "packages:", end=' ')
12
+ print(', '.join(dist.name for dist in dists))
13
+
14
+
15
+ def run():
16
+ for path in sys.path:
17
+ inspect(path)
18
+
19
+
20
+ if __name__ == '__main__':
21
+ run()
.venv/lib/python3.11/site-packages/importlib_metadata/py.typed ADDED
File without changes
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (417 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc ADDED
Binary file (306 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/_subprocess.cpython-311.pyc ADDED
Binary file (3.09 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/_types.cpython-311.pyc ADDED
Binary file (13.6 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/config.cpython-311.pyc ADDED
Binary file (26.1 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/importer.cpython-311.pyc ADDED
Binary file (2.02 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/logging.cpython-311.pyc ADDED
Binary file (8.6 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/main.cpython-311.pyc ADDED
Binary file (20.4 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/server.cpython-311.pyc ADDED
Binary file (16.9 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/__pycache__/workers.cpython-311.pyc ADDED
Binary file (6.8 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/lifespan/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (189 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/off.cpython-311.pyc ADDED
Binary file (1.14 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/lifespan/__pycache__/on.cpython-311.pyc ADDED
Binary file (8.38 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/lifespan/off.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from uvicorn import Config
6
+
7
+
8
+ class LifespanOff:
9
+ def __init__(self, config: Config) -> None:
10
+ self.should_exit = False
11
+ self.state: dict[str, Any] = {}
12
+
13
+ async def startup(self) -> None:
14
+ pass
15
+
16
+ async def shutdown(self) -> None:
17
+ pass
.venv/lib/python3.11/site-packages/uvicorn/lifespan/on.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ from asyncio import Queue
6
+ from typing import Any, Union
7
+
8
+ from uvicorn import Config
9
+ from uvicorn._types import (
10
+ LifespanScope,
11
+ LifespanShutdownCompleteEvent,
12
+ LifespanShutdownEvent,
13
+ LifespanShutdownFailedEvent,
14
+ LifespanStartupCompleteEvent,
15
+ LifespanStartupEvent,
16
+ LifespanStartupFailedEvent,
17
+ )
18
+
19
+ LifespanReceiveMessage = Union[LifespanStartupEvent, LifespanShutdownEvent]
20
+ LifespanSendMessage = Union[
21
+ LifespanStartupFailedEvent,
22
+ LifespanShutdownFailedEvent,
23
+ LifespanStartupCompleteEvent,
24
+ LifespanShutdownCompleteEvent,
25
+ ]
26
+
27
+
28
+ STATE_TRANSITION_ERROR = "Got invalid state transition on lifespan protocol."
29
+
30
+
31
+ class LifespanOn:
32
+ def __init__(self, config: Config) -> None:
33
+ if not config.loaded:
34
+ config.load()
35
+
36
+ self.config = config
37
+ self.logger = logging.getLogger("uvicorn.error")
38
+ self.startup_event = asyncio.Event()
39
+ self.shutdown_event = asyncio.Event()
40
+ self.receive_queue: Queue[LifespanReceiveMessage] = asyncio.Queue()
41
+ self.error_occured = False
42
+ self.startup_failed = False
43
+ self.shutdown_failed = False
44
+ self.should_exit = False
45
+ self.state: dict[str, Any] = {}
46
+
47
+ async def startup(self) -> None:
48
+ self.logger.info("Waiting for application startup.")
49
+
50
+ loop = asyncio.get_event_loop()
51
+ main_lifespan_task = loop.create_task(self.main()) # noqa: F841
52
+ # Keep a hard reference to prevent garbage collection
53
+ # See https://github.com/encode/uvicorn/pull/972
54
+ startup_event: LifespanStartupEvent = {"type": "lifespan.startup"}
55
+ await self.receive_queue.put(startup_event)
56
+ await self.startup_event.wait()
57
+
58
+ if self.startup_failed or (self.error_occured and self.config.lifespan == "on"):
59
+ self.logger.error("Application startup failed. Exiting.")
60
+ self.should_exit = True
61
+ else:
62
+ self.logger.info("Application startup complete.")
63
+
64
+ async def shutdown(self) -> None:
65
+ if self.error_occured:
66
+ return
67
+ self.logger.info("Waiting for application shutdown.")
68
+ shutdown_event: LifespanShutdownEvent = {"type": "lifespan.shutdown"}
69
+ await self.receive_queue.put(shutdown_event)
70
+ await self.shutdown_event.wait()
71
+
72
+ if self.shutdown_failed or (self.error_occured and self.config.lifespan == "on"):
73
+ self.logger.error("Application shutdown failed. Exiting.")
74
+ self.should_exit = True
75
+ else:
76
+ self.logger.info("Application shutdown complete.")
77
+
78
+ async def main(self) -> None:
79
+ try:
80
+ app = self.config.loaded_app
81
+ scope: LifespanScope = {
82
+ "type": "lifespan",
83
+ "asgi": {"version": self.config.asgi_version, "spec_version": "2.0"},
84
+ "state": self.state,
85
+ }
86
+ await app(scope, self.receive, self.send)
87
+ except BaseException as exc:
88
+ self.asgi = None
89
+ self.error_occured = True
90
+ if self.startup_failed or self.shutdown_failed:
91
+ return
92
+ if self.config.lifespan == "auto":
93
+ msg = "ASGI 'lifespan' protocol appears unsupported."
94
+ self.logger.info(msg)
95
+ else:
96
+ msg = "Exception in 'lifespan' protocol\n"
97
+ self.logger.error(msg, exc_info=exc)
98
+ finally:
99
+ self.startup_event.set()
100
+ self.shutdown_event.set()
101
+
102
+ async def send(self, message: LifespanSendMessage) -> None:
103
+ assert message["type"] in (
104
+ "lifespan.startup.complete",
105
+ "lifespan.startup.failed",
106
+ "lifespan.shutdown.complete",
107
+ "lifespan.shutdown.failed",
108
+ )
109
+
110
+ if message["type"] == "lifespan.startup.complete":
111
+ assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
112
+ assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
113
+ self.startup_event.set()
114
+
115
+ elif message["type"] == "lifespan.startup.failed":
116
+ assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
117
+ assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
118
+ self.startup_event.set()
119
+ self.startup_failed = True
120
+ if message.get("message"):
121
+ self.logger.error(message["message"])
122
+
123
+ elif message["type"] == "lifespan.shutdown.complete":
124
+ assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
125
+ assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
126
+ self.shutdown_event.set()
127
+
128
+ elif message["type"] == "lifespan.shutdown.failed":
129
+ assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
130
+ assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
131
+ self.shutdown_event.set()
132
+ self.shutdown_failed = True
133
+ if message.get("message"):
134
+ self.logger.error(message["message"])
135
+
136
+ async def receive(self) -> LifespanReceiveMessage:
137
+ return await self.receive_queue.get()
.venv/lib/python3.11/site-packages/uvicorn/loops/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (186 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/protocols/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/uvicorn/protocols/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (190 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/protocols/__pycache__/utils.cpython-311.pyc ADDED
Binary file (3.56 kB). View file
 
.venv/lib/python3.11/site-packages/uvicorn/protocols/http/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (195 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/auto.cpython-311.pyc ADDED
Binary file (694 Bytes). View file
 
.venv/lib/python3.11/site-packages/uvicorn/protocols/http/__pycache__/flow_control.cpython-311.pyc ADDED
Binary file (3.29 kB). View file