lamindb_setup 0.77.2__py2.py3-none-any.whl → 0.77.4__py2.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 (47) hide show
  1. lamindb_setup/__init__.py +1 -1
  2. lamindb_setup/_cache.py +34 -34
  3. lamindb_setup/_check.py +7 -7
  4. lamindb_setup/_check_setup.py +79 -79
  5. lamindb_setup/_close.py +35 -35
  6. lamindb_setup/_connect_instance.py +444 -444
  7. lamindb_setup/_delete.py +9 -5
  8. lamindb_setup/_django.py +41 -41
  9. lamindb_setup/_entry_points.py +22 -22
  10. lamindb_setup/_exportdb.py +68 -68
  11. lamindb_setup/_importdb.py +50 -50
  12. lamindb_setup/_init_instance.py +374 -374
  13. lamindb_setup/_migrate.py +239 -239
  14. lamindb_setup/_register_instance.py +36 -36
  15. lamindb_setup/_schema.py +27 -27
  16. lamindb_setup/_schema_metadata.py +411 -411
  17. lamindb_setup/_set_managed_storage.py +55 -55
  18. lamindb_setup/_setup_user.py +137 -137
  19. lamindb_setup/_silence_loggers.py +44 -44
  20. lamindb_setup/core/__init__.py +21 -21
  21. lamindb_setup/core/_aws_credentials.py +151 -151
  22. lamindb_setup/core/_aws_storage.py +48 -48
  23. lamindb_setup/core/_deprecated.py +55 -55
  24. lamindb_setup/core/_docs.py +14 -14
  25. lamindb_setup/core/_hub_core.py +590 -590
  26. lamindb_setup/core/_hub_crud.py +211 -211
  27. lamindb_setup/core/_hub_utils.py +109 -109
  28. lamindb_setup/core/_private_django_api.py +88 -88
  29. lamindb_setup/core/_settings.py +138 -138
  30. lamindb_setup/core/_settings_instance.py +467 -467
  31. lamindb_setup/core/_settings_load.py +105 -105
  32. lamindb_setup/core/_settings_save.py +81 -81
  33. lamindb_setup/core/_settings_storage.py +405 -393
  34. lamindb_setup/core/_settings_store.py +75 -75
  35. lamindb_setup/core/_settings_user.py +53 -53
  36. lamindb_setup/core/_setup_bionty_sources.py +101 -101
  37. lamindb_setup/core/cloud_sqlite_locker.py +232 -232
  38. lamindb_setup/core/django.py +114 -114
  39. lamindb_setup/core/exceptions.py +12 -12
  40. lamindb_setup/core/hashing.py +114 -114
  41. lamindb_setup/core/types.py +19 -19
  42. lamindb_setup/core/upath.py +779 -779
  43. {lamindb_setup-0.77.2.dist-info → lamindb_setup-0.77.4.dist-info}/METADATA +1 -1
  44. lamindb_setup-0.77.4.dist-info/RECORD +47 -0
  45. {lamindb_setup-0.77.2.dist-info → lamindb_setup-0.77.4.dist-info}/WHEEL +1 -1
  46. lamindb_setup-0.77.2.dist-info/RECORD +0 -47
  47. {lamindb_setup-0.77.2.dist-info → lamindb_setup-0.77.4.dist-info}/LICENSE +0 -0
@@ -1,151 +1,151 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- import time
5
-
6
- from upath.implementations.cloud import S3Path
7
-
8
- HOSTED_REGIONS = [
9
- "eu-central-1",
10
- "eu-west-2",
11
- "us-east-1",
12
- "us-east-2",
13
- "us-west-1",
14
- "us-west-2",
15
- ]
16
- lamin_env = os.getenv("LAMIN_ENV")
17
- if lamin_env is None or lamin_env == "prod":
18
- hosted_buckets_list = [f"s3://lamin-{region}" for region in HOSTED_REGIONS]
19
- hosted_buckets_list.append("s3://scverse-spatial-eu-central-1")
20
- HOSTED_BUCKETS = tuple(hosted_buckets_list)
21
- else:
22
- HOSTED_BUCKETS = ("s3://lamin-hosted-test",) # type: ignore
23
-
24
-
25
- def _keep_trailing_slash(path_str: str):
26
- return path_str if path_str[-1] == "/" else path_str + "/"
27
-
28
-
29
- AWS_CREDENTIALS_EXPIRATION = 11 * 60 * 60 # refresh credentials after 11 hours
30
-
31
-
32
- class AWSCredentialsManager:
33
- def __init__(self):
34
- self._credentials_cache = {}
35
-
36
- from s3fs import S3FileSystem
37
-
38
- # this is cached so will be resued with the connection initialized
39
- fs = S3FileSystem(cache_regions=True)
40
- fs.connect()
41
- self.anon = fs.session._credentials is None
42
-
43
- def _find_root(self, path_str: str) -> str | None:
44
- roots = self._credentials_cache.keys()
45
- if path_str in roots:
46
- return path_str
47
- roots = sorted(roots, key=len, reverse=True)
48
- for root in roots:
49
- if path_str.startswith(root):
50
- return root
51
- return None
52
-
53
- def _is_active(self, root: str) -> bool:
54
- return (
55
- time.time() - self._credentials_cache[root]["time"]
56
- ) < AWS_CREDENTIALS_EXPIRATION
57
-
58
- def _set_cached_credentials(self, root: str, credentials: dict):
59
- if root not in self._credentials_cache:
60
- self._credentials_cache[root] = {}
61
- self._credentials_cache[root]["credentials"] = credentials
62
- self._credentials_cache[root]["time"] = time.time()
63
-
64
- def _get_cached_credentials(self, root: str) -> dict:
65
- return self._credentials_cache[root]["credentials"]
66
-
67
- def _path_inject_options(self, path: S3Path, credentials: dict) -> S3Path:
68
- if credentials == {}:
69
- # credentials were specified manually for the path
70
- if "anon" in path.storage_options:
71
- anon = path.storage_options["anon"]
72
- elif path.fs.key is not None and path.fs.secret is not None:
73
- anon = False
74
- else:
75
- anon = self.anon
76
- connection_options = {"anon": anon}
77
- else:
78
- connection_options = credentials
79
-
80
- if "cache_regions" in path.storage_options:
81
- cache_regions = path.storage_options["cache_regions"]
82
- else:
83
- cache_regions = True
84
-
85
- return S3Path(path, cache_regions=cache_regions, **connection_options)
86
-
87
- def enrich_path(self, path: S3Path, access_token: str | None = None) -> S3Path:
88
- # trailing slash is needed to avoid returning incorrect results
89
- # with .startswith
90
- # for example s3://lamindata-eu should not receive cache for s3://lamindata
91
- path_str = _keep_trailing_slash(path.as_posix())
92
- root = self._find_root(path_str)
93
-
94
- if root is not None:
95
- set_cache = False
96
- credentials = self._get_cached_credentials(root)
97
-
98
- if access_token is not None:
99
- set_cache = True
100
- elif credentials != {}:
101
- # update credentials
102
- if not self._is_active(root):
103
- set_cache = True
104
- else:
105
- set_cache = True
106
-
107
- if set_cache:
108
- from ._hub_core import access_aws
109
- from ._settings import settings
110
-
111
- if settings.user.handle != "anonymous" or access_token is not None:
112
- storage_root_info = access_aws(path_str, access_token=access_token)
113
- else:
114
- storage_root_info = {"credentials": {}, "accessibility": {}}
115
-
116
- accessibility = storage_root_info["accessibility"]
117
- is_managed = accessibility.get("is_managed", False)
118
- if is_managed:
119
- credentials = storage_root_info["credentials"]
120
- else:
121
- credentials = {}
122
-
123
- if access_token is None:
124
- if "storage_root" in accessibility:
125
- root = accessibility["storage_root"]
126
- # just to be safe
127
- root = None if root == "" else root
128
- if root is None:
129
- # heuristic
130
- # do not write the first level for the known hosted buckets
131
- if path_str.startswith(HOSTED_BUCKETS):
132
- root = "/".join(path.path.rstrip("/").split("/")[:2])
133
- else:
134
- # write the bucket for everything else
135
- root = path._url.netloc
136
- root = "s3://" + root
137
- self._set_cached_credentials(_keep_trailing_slash(root), credentials)
138
-
139
- return self._path_inject_options(path, credentials)
140
-
141
-
142
- _aws_credentials_manager: AWSCredentialsManager | None = None
143
-
144
-
145
- def get_aws_credentials_manager() -> AWSCredentialsManager:
146
- global _aws_credentials_manager
147
-
148
- if _aws_credentials_manager is None:
149
- _aws_credentials_manager = AWSCredentialsManager()
150
-
151
- return _aws_credentials_manager
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import time
5
+
6
+ from upath.implementations.cloud import S3Path
7
+
8
+ HOSTED_REGIONS = [
9
+ "eu-central-1",
10
+ "eu-west-2",
11
+ "us-east-1",
12
+ "us-east-2",
13
+ "us-west-1",
14
+ "us-west-2",
15
+ ]
16
+ lamin_env = os.getenv("LAMIN_ENV")
17
+ if lamin_env is None or lamin_env == "prod":
18
+ hosted_buckets_list = [f"s3://lamin-{region}" for region in HOSTED_REGIONS]
19
+ hosted_buckets_list.append("s3://scverse-spatial-eu-central-1")
20
+ HOSTED_BUCKETS = tuple(hosted_buckets_list)
21
+ else:
22
+ HOSTED_BUCKETS = ("s3://lamin-hosted-test",) # type: ignore
23
+
24
+
25
+ def _keep_trailing_slash(path_str: str):
26
+ return path_str if path_str[-1] == "/" else path_str + "/"
27
+
28
+
29
+ AWS_CREDENTIALS_EXPIRATION = 11 * 60 * 60 # refresh credentials after 11 hours
30
+
31
+
32
+ class AWSCredentialsManager:
33
+ def __init__(self):
34
+ self._credentials_cache = {}
35
+
36
+ from s3fs import S3FileSystem
37
+
38
+ # this is cached so will be resued with the connection initialized
39
+ fs = S3FileSystem(cache_regions=True)
40
+ fs.connect()
41
+ self.anon = fs.session._credentials is None
42
+
43
+ def _find_root(self, path_str: str) -> str | None:
44
+ roots = self._credentials_cache.keys()
45
+ if path_str in roots:
46
+ return path_str
47
+ roots = sorted(roots, key=len, reverse=True)
48
+ for root in roots:
49
+ if path_str.startswith(root):
50
+ return root
51
+ return None
52
+
53
+ def _is_active(self, root: str) -> bool:
54
+ return (
55
+ time.time() - self._credentials_cache[root]["time"]
56
+ ) < AWS_CREDENTIALS_EXPIRATION
57
+
58
+ def _set_cached_credentials(self, root: str, credentials: dict):
59
+ if root not in self._credentials_cache:
60
+ self._credentials_cache[root] = {}
61
+ self._credentials_cache[root]["credentials"] = credentials
62
+ self._credentials_cache[root]["time"] = time.time()
63
+
64
+ def _get_cached_credentials(self, root: str) -> dict:
65
+ return self._credentials_cache[root]["credentials"]
66
+
67
+ def _path_inject_options(self, path: S3Path, credentials: dict) -> S3Path:
68
+ if credentials == {}:
69
+ # credentials were specified manually for the path
70
+ if "anon" in path.storage_options:
71
+ anon = path.storage_options["anon"]
72
+ elif path.fs.key is not None and path.fs.secret is not None:
73
+ anon = False
74
+ else:
75
+ anon = self.anon
76
+ connection_options = {"anon": anon}
77
+ else:
78
+ connection_options = credentials
79
+
80
+ if "cache_regions" in path.storage_options:
81
+ cache_regions = path.storage_options["cache_regions"]
82
+ else:
83
+ cache_regions = True
84
+
85
+ return S3Path(path, cache_regions=cache_regions, **connection_options)
86
+
87
+ def enrich_path(self, path: S3Path, access_token: str | None = None) -> S3Path:
88
+ # trailing slash is needed to avoid returning incorrect results
89
+ # with .startswith
90
+ # for example s3://lamindata-eu should not receive cache for s3://lamindata
91
+ path_str = _keep_trailing_slash(path.as_posix())
92
+ root = self._find_root(path_str)
93
+
94
+ if root is not None:
95
+ set_cache = False
96
+ credentials = self._get_cached_credentials(root)
97
+
98
+ if access_token is not None:
99
+ set_cache = True
100
+ elif credentials != {}:
101
+ # update credentials
102
+ if not self._is_active(root):
103
+ set_cache = True
104
+ else:
105
+ set_cache = True
106
+
107
+ if set_cache:
108
+ from ._hub_core import access_aws
109
+ from ._settings import settings
110
+
111
+ if settings.user.handle != "anonymous" or access_token is not None:
112
+ storage_root_info = access_aws(path_str, access_token=access_token)
113
+ else:
114
+ storage_root_info = {"credentials": {}, "accessibility": {}}
115
+
116
+ accessibility = storage_root_info["accessibility"]
117
+ is_managed = accessibility.get("is_managed", False)
118
+ if is_managed:
119
+ credentials = storage_root_info["credentials"]
120
+ else:
121
+ credentials = {}
122
+
123
+ if access_token is None:
124
+ if "storage_root" in accessibility:
125
+ root = accessibility["storage_root"]
126
+ # just to be safe
127
+ root = None if root == "" else root
128
+ if root is None:
129
+ # heuristic
130
+ # do not write the first level for the known hosted buckets
131
+ if path_str.startswith(HOSTED_BUCKETS):
132
+ root = "/".join(path.path.rstrip("/").split("/")[:2])
133
+ else:
134
+ # write the bucket for everything else
135
+ root = path._url.netloc
136
+ root = "s3://" + root
137
+ self._set_cached_credentials(_keep_trailing_slash(root), credentials)
138
+
139
+ return self._path_inject_options(path, credentials)
140
+
141
+
142
+ _aws_credentials_manager: AWSCredentialsManager | None = None
143
+
144
+
145
+ def get_aws_credentials_manager() -> AWSCredentialsManager:
146
+ global _aws_credentials_manager
147
+
148
+ if _aws_credentials_manager is None:
149
+ _aws_credentials_manager = AWSCredentialsManager()
150
+
151
+ return _aws_credentials_manager
@@ -1,48 +1,48 @@
1
- from __future__ import annotations
2
-
3
-
4
- def get_location(ip="ipinfo.io"):
5
- import requests # type: ignore
6
-
7
- response = requests.get(f"http://{ip}/json").json()
8
- loc = response["loc"].split(",")
9
- return {"latitude": float(loc[0]), "longitude": float(loc[1])}
10
-
11
-
12
- def haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
13
- import math
14
-
15
- R = 6371 # Radius of the Earth in kilometers
16
- dLat = math.radians(lat2 - lat1)
17
- dLon = math.radians(lon2 - lon1)
18
- a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.cos(
19
- math.radians(lat1)
20
- ) * math.cos(math.radians(lat2)) * math.sin(dLon / 2) * math.sin(dLon / 2)
21
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
22
- distance = R * c
23
- return distance
24
-
25
-
26
- def find_closest_aws_region() -> str:
27
- aws_region_locations = {
28
- "us-east-1": {"latitude": 39.0, "longitude": -77.0}, # Northern Virginia
29
- "us-east-2": {"latitude": 40.0, "longitude": -83.0}, # Ohio
30
- "us-west-1": {"latitude": 37.77, "longitude": -122.41}, # Northern California
31
- "us-west-2": {"latitude": 45.52, "longitude": -122.68}, # Oregon
32
- "eu-central-1": {"latitude": 50.11, "longitude": 8.68}, # Frankfurt
33
- "eu-west-2": {"latitude": 51.51, "longitude": -0.13}, # London, UK
34
- }
35
- your_location = get_location()
36
- closest_region = ""
37
- min_distance = float("inf")
38
- for region in aws_region_locations:
39
- region_location = aws_region_locations[region]
40
- distance = haversine(
41
- your_location["latitude"],
42
- your_location["longitude"],
43
- region_location["latitude"],
44
- region_location["longitude"],
45
- )
46
- if distance < min_distance:
47
- closest_region, min_distance = region, distance
48
- return closest_region
1
+ from __future__ import annotations
2
+
3
+
4
+ def get_location(ip="ipinfo.io"):
5
+ import requests # type: ignore
6
+
7
+ response = requests.get(f"http://{ip}/json").json()
8
+ loc = response["loc"].split(",")
9
+ return {"latitude": float(loc[0]), "longitude": float(loc[1])}
10
+
11
+
12
+ def haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
13
+ import math
14
+
15
+ R = 6371 # Radius of the Earth in kilometers
16
+ dLat = math.radians(lat2 - lat1)
17
+ dLon = math.radians(lon2 - lon1)
18
+ a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.cos(
19
+ math.radians(lat1)
20
+ ) * math.cos(math.radians(lat2)) * math.sin(dLon / 2) * math.sin(dLon / 2)
21
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
22
+ distance = R * c
23
+ return distance
24
+
25
+
26
+ def find_closest_aws_region() -> str:
27
+ aws_region_locations = {
28
+ "us-east-1": {"latitude": 39.0, "longitude": -77.0}, # Northern Virginia
29
+ "us-east-2": {"latitude": 40.0, "longitude": -83.0}, # Ohio
30
+ "us-west-1": {"latitude": 37.77, "longitude": -122.41}, # Northern California
31
+ "us-west-2": {"latitude": 45.52, "longitude": -122.68}, # Oregon
32
+ "eu-central-1": {"latitude": 50.11, "longitude": 8.68}, # Frankfurt
33
+ "eu-west-2": {"latitude": 51.51, "longitude": -0.13}, # London, UK
34
+ }
35
+ your_location = get_location()
36
+ closest_region = ""
37
+ min_distance = float("inf")
38
+ for region in aws_region_locations:
39
+ region_location = aws_region_locations[region]
40
+ distance = haversine(
41
+ your_location["latitude"],
42
+ your_location["longitude"],
43
+ region_location["latitude"],
44
+ region_location["longitude"],
45
+ )
46
+ if distance < min_distance:
47
+ closest_region, min_distance = region, distance
48
+ return closest_region
@@ -1,55 +1,55 @@
1
- from __future__ import annotations
2
-
3
- # BSD 3-Clause License
4
- # Copyright (c) 2017-2018 P. Angerer, F. Alexander Wolf, Theis Lab
5
- # All rights reserved.
6
- # Redistribution and use in source and binary forms, with or without
7
- # modification, are permitted provided that the following conditions are met:
8
- # * Redistributions of source code must retain the above copyright notice, this
9
- # list of conditions and the following disclaimer.
10
- # * Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # * Neither the name of the copyright holder nor the names of its
14
- # contributors may be used to endorse or promote products derived from
15
- # this software without specific prior written permission.
16
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
- # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
- # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
- # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
- # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
- # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
- import warnings
27
- from functools import wraps
28
-
29
-
30
- def deprecated(new_name: str):
31
- """Deprecated.
32
-
33
- This is a decorator which can be used to mark functions
34
- as deprecated. It will result in a warning being emitted
35
- when the function is used.
36
- """
37
-
38
- def decorator(func):
39
- @wraps(func)
40
- def new_func(*args, **kwargs):
41
- # turn off filter
42
- warnings.simplefilter("always", DeprecationWarning)
43
- warnings.warn(
44
- f"Use {new_name} instead of {func.__name__}, "
45
- f"{func.__name__} will be removed in the future.",
46
- category=DeprecationWarning,
47
- stacklevel=2,
48
- )
49
- warnings.simplefilter("default", DeprecationWarning) # reset filter
50
- return func(*args, **kwargs)
51
-
52
- setattr(new_func, "__deprecated", True)
53
- return new_func
54
-
55
- return decorator
1
+ from __future__ import annotations
2
+
3
+ # BSD 3-Clause License
4
+ # Copyright (c) 2017-2018 P. Angerer, F. Alexander Wolf, Theis Lab
5
+ # All rights reserved.
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ # * Redistributions of source code must retain the above copyright notice, this
9
+ # list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder nor the names of its
14
+ # contributors may be used to endorse or promote products derived from
15
+ # this software without specific prior written permission.
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ import warnings
27
+ from functools import wraps
28
+
29
+
30
+ def deprecated(new_name: str):
31
+ """Deprecated.
32
+
33
+ This is a decorator which can be used to mark functions
34
+ as deprecated. It will result in a warning being emitted
35
+ when the function is used.
36
+ """
37
+
38
+ def decorator(func):
39
+ @wraps(func)
40
+ def new_func(*args, **kwargs):
41
+ # turn off filter
42
+ warnings.simplefilter("always", DeprecationWarning)
43
+ warnings.warn(
44
+ f"Use {new_name} instead of {func.__name__}, "
45
+ f"{func.__name__} will be removed in the future.",
46
+ category=DeprecationWarning,
47
+ stacklevel=2,
48
+ )
49
+ warnings.simplefilter("default", DeprecationWarning) # reset filter
50
+ return func(*args, **kwargs)
51
+
52
+ setattr(new_func, "__deprecated", True)
53
+ return new_func
54
+
55
+ return decorator
@@ -1,14 +1,14 @@
1
- from __future__ import annotations
2
-
3
- from textwrap import dedent
4
-
5
-
6
- def doc_args(*args):
7
- """Pass arguments to docstrings."""
8
-
9
- def dec(obj):
10
- obj.__orig_doc__ = obj.__doc__
11
- obj.__doc__ = dedent(obj.__doc__).format(*args)
12
- return obj
13
-
14
- return dec
1
+ from __future__ import annotations
2
+
3
+ from textwrap import dedent
4
+
5
+
6
+ def doc_args(*args):
7
+ """Pass arguments to docstrings."""
8
+
9
+ def dec(obj):
10
+ obj.__orig_doc__ = obj.__doc__
11
+ obj.__doc__ = dedent(obj.__doc__).format(*args)
12
+ return obj
13
+
14
+ return dec