datashare-python 0.12.6__tar.gz → 0.12.8__tar.gz

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 (30) hide show
  1. {datashare_python-0.12.6 → datashare_python-0.12.8}/PKG-INFO +1 -1
  2. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/config.py +4 -1
  3. datashare_python-0.12.8/datashare_python/constants.py +212 -0
  4. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/objects.py +9 -10
  5. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/utils.py +36 -12
  6. datashare_python-0.12.8/datashare_python/worker-template.tar.gz +0 -0
  7. {datashare_python-0.12.6 → datashare_python-0.12.8}/pyproject.toml +1 -1
  8. datashare_python-0.12.6/datashare_python/constants.py +0 -16
  9. datashare_python-0.12.6/datashare_python/worker-template.tar.gz +0 -0
  10. {datashare_python-0.12.6 → datashare_python-0.12.8}/.gitignore +0 -0
  11. {datashare_python-0.12.6 → datashare_python-0.12.8}/README.md +0 -0
  12. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/.gitignore +0 -0
  13. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/__init__.py +0 -0
  14. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/__main__.py +0 -0
  15. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/cli/__init__.py +0 -0
  16. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/cli/project.py +0 -0
  17. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/cli/task.py +0 -0
  18. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/cli/utils.py +0 -0
  19. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/cli/worker.py +0 -0
  20. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/conftest.py +0 -0
  21. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/dependencies.py +0 -0
  22. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/discovery.py +0 -0
  23. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/exceptions.py +0 -0
  24. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/interceptors.py +0 -0
  25. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/logging_.py +0 -0
  26. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/mimetypes_.py +0 -0
  27. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/task_client.py +0 -0
  28. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/template.py +0 -0
  29. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/types_.py +0 -0
  30. {datashare_python-0.12.6 → datashare_python-0.12.8}/datashare_python/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datashare-python
3
- Version: 0.12.6
3
+ Version: 0.12.8
4
4
  Summary: Manage Python tasks and local resources in Datashare
5
5
  Project-URL: Homepage, https://icij.github.io/datashare-python/
6
6
  Project-URL: Documentation, https://icij.github.io/datashare-python/
@@ -114,6 +114,7 @@ class LoggingConfig(BaseModel):
114
114
 
115
115
  class ResourceCacheConfig(BaseModel):
116
116
  size: int = 1
117
+ refresh_interval: int | None = None
117
118
  exit_context_managers: bool = True
118
119
 
119
120
  def to_resource_cache(self) -> "SharedResources":
@@ -123,7 +124,9 @@ class ResourceCacheConfig(BaseModel):
123
124
  if self.exit_context_managers:
124
125
  eviction_callback = close_cm_callback
125
126
  return SharedResources(
126
- cache_size=self.size, eviction_callback=eviction_callback
127
+ cache_size=self.size,
128
+ eviction_callback=eviction_callback,
129
+ refresh_interval=self.refresh_interval,
127
130
  )
128
131
 
129
132
 
@@ -0,0 +1,212 @@
1
+ from copy import deepcopy
2
+ from pathlib import Path
3
+
4
+ PACKAGE_DIR = Path(__file__).parent
5
+ PACKAGE_ROOT = PACKAGE_DIR.parent
6
+
7
+
8
+ DEFAULT_TEMPORAL_ADDRESS = "temporal:7233"
9
+
10
+ DEFAULT_DS_ADDRESS = "http://localhost:8080"
11
+
12
+ DEFAULT_NAMESPACE = "datashare-default"
13
+
14
+ METADATA_JSON = "metadata.json"
15
+ MANIFEST_JSON = "manifest.json"
16
+
17
+ TIKA_METADATA_RESOURCENAME = "tika_metadata_resourcename"
18
+
19
+
20
+ _LANGUAGES = {
21
+ "ABKHAZIAN": ("ab", "abk"),
22
+ "AFAR": ("aa", "aar"),
23
+ "AFRIKAANS": ("af", "afr"),
24
+ "AKAN": ("ak", "aka"),
25
+ "ALBANIAN": ("sq", "sqi"),
26
+ "AMHARIC": ("am", "amh"),
27
+ "ARABIC": ("ar", "ara"),
28
+ "ARAGONESE": ("an", "arg"),
29
+ "ARMENIAN": ("hy", "hye"),
30
+ "ASSAMESE": ("as", "asm"),
31
+ "AVARIC": ("av", "ava"),
32
+ "AVESTAN": ("ae", "ave"),
33
+ "AYMARA": ("ay", "aym"),
34
+ "AZERBAIJANI": ("az", "aze"),
35
+ "BAMBARA": ("bm", "bam"),
36
+ "BASHKIR": ("ba", "bak"),
37
+ "BASQUE": ("eu", "eus"),
38
+ "BELARUSIAN": ("be", "bel"),
39
+ "BENGALI": ("bn", "ben"),
40
+ "BIHARI": ("bh", "bih"),
41
+ "BISLAMA": ("bi", "bis"),
42
+ "BOSNIAN": ("bs", "bos"),
43
+ "BRETON": ("br", "bre"),
44
+ "BULGARIAN": ("bg", "bul"),
45
+ "BURMESE": ("my", "mya"),
46
+ "CATALAN": ("ca", "cat"),
47
+ "CHAMORRO": ("ch", "cha"),
48
+ "CHECHEN": ("ce", "che"),
49
+ "CHICHEWA": ("ny", "nya"),
50
+ "CHINESE": ("zh", "zho"),
51
+ "CHUVASH": ("cv", "chv"),
52
+ "CORNISH": ("kw", "cor"),
53
+ "CORSICAN": ("co", "cos"),
54
+ "CREE": ("cr", "cre"),
55
+ "CROATIAN": ("hr", "hrv"),
56
+ "CZECH": ("cs", "ces"),
57
+ "DANISH": ("da", "dan"),
58
+ "MALDIVIAN": ("dv", "div"),
59
+ "DUTCH": ("nl", "nld"),
60
+ "DZONGKHA": ("dz", "dzo"),
61
+ "ENGLISH": ("en", "eng"),
62
+ "ESPERANTO": ("eo", "epo"),
63
+ "ESTONIAN": ("et", "est"),
64
+ "EWE": ("ee", "ewe"),
65
+ "FAROESE": ("fo", "fao"),
66
+ "FIJIAN": ("fj", "fij"),
67
+ "FINNISH": ("fi", "fin"),
68
+ "FRENCH": ("fr", "fra"),
69
+ "FULAH": ("ff", "ful"),
70
+ "GALICIAN": ("gl", "glg"),
71
+ "GEORGIAN": ("ka", "kat"),
72
+ "GERMAN": ("de", "deu"),
73
+ "GREEK": ("el", "ell"),
74
+ "GUARANI": ("gn", "grn"),
75
+ "GUJARATI": ("gu", "guj"),
76
+ "HAITIAN": ("ht", "hat"),
77
+ "HAUSA": ("ha", "hau"),
78
+ "HEBREW": ("he", "heb"),
79
+ "HERERO": ("hz", "her"),
80
+ "HINDI": ("hi", "hin"),
81
+ "HIRIMOTU": ("ho", "hmo"),
82
+ "HUNGARIAN": ("hu", "hun"),
83
+ "INTERLINGUA": ("ia", "ina"),
84
+ "INDONESIAN": ("id", "ind"),
85
+ "INTERLIGUE": ("ie", "ile"),
86
+ "IRISH": ("ga", "gle"),
87
+ "IGBO": ("ig", "ibo"),
88
+ "INUPIAQ": ("ik", "ipk"),
89
+ "IDO": ("io", "ido"),
90
+ "ICELANDIC": ("is", "isl"),
91
+ "ITALIAN": ("it", "ita"),
92
+ "INUKTITUT": ("iu", "iku"),
93
+ "JAPANESE": ("ja", "jpn"),
94
+ "JAVANESE": ("jv", "jav"),
95
+ "GREENLANDIC": ("kl", "kal"),
96
+ "KANNADA": ("kn", "kan"),
97
+ "KANURI": ("kr", "kau"),
98
+ "KASHMIRI": ("ks", "kas"),
99
+ "KAZAKH": ("kk", "kaz"),
100
+ "CENTRALKHMER": ("km", "khm"),
101
+ "KIKUYU": ("ki", "kik"),
102
+ "KINYARWANDA": ("rw", "kin"),
103
+ "KIRGHIZ": ("ky", "kir"),
104
+ "KOMI": ("kv", "kom"),
105
+ "KONGO": ("kg", "kon"),
106
+ "KOREAN": ("ko", "kor"),
107
+ "KURDISH": ("ku", "kur"),
108
+ "KUANYAMA": ("kj", "kua"),
109
+ "LATIN": ("la", "lat"),
110
+ "LUXEMBOURGISH": ("lb", "ltz"),
111
+ "GANDA": ("lg", "lug"),
112
+ "LIMBURGAN": ("li", "lim"),
113
+ "LINGALA": ("ln", "lin"),
114
+ "LAO": ("lo", "lao"),
115
+ "LITHUANIAN": ("lt", "lit"),
116
+ "LUBAKATANGA": ("lu", "lub"),
117
+ "LATVIAN": ("lv", "lav"),
118
+ "MANX": ("gv", "glv"),
119
+ "MACEDONIAN": ("mk", "mkd"),
120
+ "MALAGASY": ("mg", "mlg"),
121
+ "MALAY": ("ms", "msa"),
122
+ "MALAYALAM": ("ml", "mal"),
123
+ "MALTESE": ("mt", "mlt"),
124
+ "MAORI": ("mi", "mri"),
125
+ "MARATHI": ("mr", "mar"),
126
+ "MARSHALLESE": ("mh", "mah"),
127
+ "MONGOLIAN": ("mn", "mon"),
128
+ "NAURU": ("na", "nau"),
129
+ "NAVAJO": ("nv", "nav"),
130
+ "NORTHNDEBELE": ("nd", "nde"),
131
+ "NEPALI": ("ne", "nep"),
132
+ "NDONGA": ("ng", "ndo"),
133
+ "NORWEGIANBOKMAL": ("nb", "nob"),
134
+ "NORWEGIANNYNORSK": ("nn", "nno"),
135
+ "NORWEGIAN": ("no", "nor"),
136
+ "SICHUANYI": ("ii", "iii"),
137
+ "SOUTHNDEBELE": ("nr", "nbl"),
138
+ "OCCITAN": ("oc", "oci"),
139
+ "OJIBWA": ("oj", "oji"),
140
+ "OLDSLAVONIC": ("cu", "chu"),
141
+ "OROMO": ("om", "orm"),
142
+ "ORIYA": ("or", "ori"),
143
+ "OSSETIAN": ("os", "oss"),
144
+ "PANJABI": ("pa", "pan"),
145
+ "PALI": ("pi", "pli"),
146
+ "PERSIAN": ("fa", "fas"),
147
+ "POLISH": ("pl", "pol"),
148
+ "PASHTO": ("ps", "pus"),
149
+ "PORTUGUESE": ("pt", "por"),
150
+ "QUECHUA": ("qu", "que"),
151
+ "ROMANSH": ("rm", "roh"),
152
+ "ROMANIAN": ("ro", "ron"),
153
+ "RUNDI": ("rn", "run"),
154
+ "MOLDAVIAN": ("ro", "ron"),
155
+ "MOLDOVAN": ("ro", "ron"),
156
+ "RUSSIAN": ("ru", "rus"),
157
+ "SANSKRIT": ("sa", "san"),
158
+ "SARDINIAN": ("sc", "srd"),
159
+ "SINDHI": ("sd", "snd"),
160
+ "NORTHERNSAMI": ("se", "sme"),
161
+ "SAMOAN": ("sm", "smo"),
162
+ "SANGO": ("sg", "sag"),
163
+ "SERBIAN": ("sr", "srp"),
164
+ "GAELIC": ("gd", "gla"),
165
+ "SHONA": ("sn", "sna"),
166
+ "SINHALESE": ("si", "sin"),
167
+ "SLOVAK": ("sk", "slk"),
168
+ "SLOVENIAN": ("sl", "slv"),
169
+ "SOMALI": ("so", "som"),
170
+ "SOUTHERNSOTHO": ("st", "sot"),
171
+ "SPANISH": ("es", "spa"),
172
+ "SUNDANESE": ("su", "sun"),
173
+ "SWAHILI": ("sw", "swa"),
174
+ "SWATI": ("ss", "ssw"),
175
+ "SWEDISH": ("sv", "swe"),
176
+ "TAMIL": ("ta", "tam"),
177
+ "TELUGU": ("te", "tel"),
178
+ "TAJIK": ("tg", "tgk"),
179
+ "THAI": ("th", "tha"),
180
+ "TIGRINYA": ("ti", "tir"),
181
+ "TIBETAN": ("bo", "bod"),
182
+ "TURKMEN": ("tk", "tuk"),
183
+ "TAGALOG": ("tl", "tgl"),
184
+ "TSWANA": ("tn", "tsn"),
185
+ "TONGA": ("to", "ton"),
186
+ "TURKISH": ("tr", "tur"),
187
+ "TSONGA": ("ts", "tso"),
188
+ "TATAR": ("tt", "tat"),
189
+ "TWI": ("tw", "twi"),
190
+ "TAHITIAN": ("ty", "tah"),
191
+ "UIGHUR": ("ug", "uig"),
192
+ "UKRAINIAN": ("uk", "ukr"),
193
+ "URDU": ("ur", "urd"),
194
+ "UZBEK": ("uz", "uzb"),
195
+ "VENDA": ("ve", "ven"),
196
+ "VIETNAMESE": ("vi", "vie"),
197
+ "VOLAPUK": ("vo", "vol"),
198
+ "WALLOON": ("wa", "wln"),
199
+ "WELSH": ("cy", "cym"),
200
+ "WOLOF": ("wo", "wol"),
201
+ "WESTERNFRISIAN": ("fy", "fry"),
202
+ "XHOSA": ("xh", "xho"),
203
+ "YIDDISH": ("yi", "yid"),
204
+ "YORUBA": ("yo", "yor"),
205
+ "ZHUANG": ("za", "zha"),
206
+ "ZULU": ("zu", "zul"),
207
+ "UNKNOWN": ("un", "ukn"),
208
+ }
209
+
210
+
211
+ def get_datashare_languages() -> dict[str, tuple[str, str]]:
212
+ return deepcopy(_LANGUAGES)
@@ -14,12 +14,11 @@ from typing import Annotated, Any, ClassVar, Literal, Self, TypeVar, cast, final
14
14
 
15
15
  import langcodes
16
16
  from icij_common.registrable import Registrable
17
- from pydantic_core import PydanticCustomError, ValidationError, core_schema
17
+ from pydantic_core import PydanticCustomError, core_schema
18
18
  from pydantic_core.core_schema import PlainValidatorFunctionSchema
19
- from pydantic_extra_types.language_code import LanguageName
20
19
  from temporalio import activity, workflow
21
20
 
22
- from .constants import TIKA_METADATA_RESOURCENAME
21
+ from .constants import TIKA_METADATA_RESOURCENAME, get_datashare_languages
23
22
 
24
23
  with workflow.unsafe.imports_passed_through():
25
24
  from icij_common.es import (
@@ -81,7 +80,7 @@ class DatashareModel(BaseModel):
81
80
 
82
81
 
83
82
  class DatashareLanguage(str):
84
- _language_type_adapter: ClassVar[TypeAdapter] = TypeAdapter(LanguageName)
83
+ _languages: ClassVar[dict[str, tuple[str, str]]] = get_datashare_languages()
85
84
 
86
85
  @classmethod
87
86
  def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> Self:
@@ -91,8 +90,8 @@ class DatashareLanguage(str):
91
90
  )
92
91
  try:
93
92
  # Use pydantic provided validation
94
- cls._language_type_adapter.validate_python(__input_value.title())
95
- except ValidationError as e:
93
+ cls._languages[__input_value]
94
+ except KeyError as e:
96
95
  raise PydanticCustomError(
97
96
  "datashare_language", "Unknown Datashare language"
98
97
  ) from e
@@ -109,16 +108,16 @@ class DatashareLanguage(str):
109
108
  )
110
109
 
111
110
  @property
112
- def as_language_name(self) -> LanguageName:
113
- return LanguageName(self.title())
111
+ def as_language_name(self) -> str:
112
+ return self
114
113
 
115
114
  @property
116
115
  def alpha2(self) -> str | None:
117
- return self.as_language_name.alpha2
116
+ return self._languages[self][0]
118
117
 
119
118
  @property
120
119
  def alpha3(self) -> str:
121
- return self.as_language_name.alpha3
120
+ return self._languages[self][1]
122
121
 
123
122
 
124
123
  @unique
@@ -822,9 +822,11 @@ class SharedResources:
822
822
  self,
823
823
  cache_size: int = 1,
824
824
  eviction_callback: Callable[[Any, Any], None] | None = None,
825
+ refresh_interval: int | None = None,
825
826
  ) -> None:
826
827
  self._eviction_callback = eviction_callback
827
828
  self._cache = LRU(cache_size, self._on_evict)
829
+ self._refresh_interval = refresh_interval
828
830
  self._sentinel = object()
829
831
  # Leased resources are tracked by id, evicting a leased resource defers the
830
832
  # eviction callback until its last lease is released
@@ -854,6 +856,14 @@ class SharedResources:
854
856
  """
855
857
  value = self._get_and_acquire(key)
856
858
  if value is self._sentinel:
859
+ # The factory can be long to complete (that's one of the reason we want to
860
+ # cache its results). Because we don't lock the factory call, together with
861
+ # the cache update, in case of concurrent access, quicker factories will
862
+ # complete first and will stay longer in the cache. This is fine, the
863
+ # alternative is to lock factory + cache update to avoid concurrent access
864
+ # but this will mean waiting for the first factory call to complete, this
865
+ # can potentially imply longer waits than no getting the value from the
866
+ # cache
857
867
  value = default_factory()
858
868
  self._cache_and_acquire(key, value)
859
869
  try:
@@ -873,6 +883,14 @@ class SharedResources:
873
883
  """
874
884
  value = self._get_and_acquire(key)
875
885
  if value is self._sentinel:
886
+ # The factory can be long to complete (that's one of the reason we want to
887
+ # cache its results). Because we don't lock the factory call, together with
888
+ # the cache update, in case of concurrent access, quicker factories will
889
+ # complete first and will stay longer in the cache. This is fine, the
890
+ # alternative is to lock factory + cache update to avoid concurrent access
891
+ # but this will mean waiting for the first factory call to complete, this
892
+ # can potentially imply longer waits than no getting the value from the
893
+ # cache
876
894
  value = await default_factory()
877
895
  self._cache_and_acquire(key, value)
878
896
  try:
@@ -884,28 +902,31 @@ class SharedResources:
884
902
  with self._lock:
885
903
  value = self._cache.get(key, self._sentinel)
886
904
  if value is not self._sentinel:
887
- self._acquire(value)
905
+ # If refresh needs to happen, we set the value to the sentinel.
906
+ # The old value will be release with the last user call to _release.
907
+ # Returning the sentinel will trigger the call to the factory
908
+ if (
909
+ self._refresh_interval is not None
910
+ and self._n_leases[self._vkey(value)] >= self._refresh_interval
911
+ ):
912
+ self._pending_evictions[self._vkey(value)] = (key, value)
913
+ value = self._sentinel
914
+ else:
915
+ self._acquire(value)
888
916
  return value
889
917
 
890
918
  def _cache_and_acquire(self, key: str, value: Any) -> None:
891
- # The factory can be long to complete (that's one of the reason we want to
892
- # cache its results). Because we don't lock the factory call, together with
893
- # the cache update, in case of concurrent access, quicker factories will
894
- # complete first and will stay longer in the cache. This is fine, the
895
- # alternative is to lock factory + cache update to avoid concurrent access but
896
- # this will mean waiting for the first factory call to complete, this can
897
- # potentially imply longer waits than no getting the value from the cache
898
919
  with self._lock:
899
920
  self._acquire(value)
900
921
  # Can evict other resources, must be called without holding the lock
901
922
  self._cache[key] = value
902
923
 
903
924
  def _acquire(self, value: Any) -> None:
904
- value_id = id(value)
925
+ value_id = self._vkey(value)
905
926
  self._n_leases[value_id] = self._n_leases.get(value_id, 0) + 1
906
927
 
907
928
  def _release(self, value: Any) -> None:
908
- value_id = id(value)
929
+ value_id = self._vkey(value)
909
930
  with self._lock:
910
931
  self._n_leases[value_id] -= 1
911
932
  if self._n_leases[value_id]:
@@ -917,8 +938,8 @@ class SharedResources:
917
938
 
918
939
  def _on_evict(self, key: Any, value: Any) -> None:
919
940
  with self._lock:
920
- if id(value) in self._n_leases:
921
- self._pending_evictions[id(value)] = (key, value)
941
+ if self._vkey(value) in self._n_leases:
942
+ self._pending_evictions[self._vkey(value)] = (key, value)
922
943
  return
923
944
  self._evict(key, value)
924
945
 
@@ -926,6 +947,9 @@ class SharedResources:
926
947
  if self._eviction_callback is not None:
927
948
  self._eviction_callback(key, value)
928
949
 
950
+ def _vkey(self, value: Any) -> int:
951
+ return id(value)
952
+
929
953
 
930
954
  def close_cm_callback(key: str, value: Any) -> None: # noqa: ARG001
931
955
  if hasattr(value, "__exit__"):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datashare-python"
3
- version = "0.12.6"
3
+ version = "0.12.8"
4
4
  description = "Manage Python tasks and local resources in Datashare"
5
5
  authors = [
6
6
  { name = "Clément Doumouro", email = "cdoumouro@icij.org" },
@@ -1,16 +0,0 @@
1
- from pathlib import Path
2
-
3
- PACKAGE_DIR = Path(__file__).parent
4
- PACKAGE_ROOT = PACKAGE_DIR.parent
5
-
6
-
7
- DEFAULT_TEMPORAL_ADDRESS = "temporal:7233"
8
-
9
- DEFAULT_DS_ADDRESS = "http://localhost:8080"
10
-
11
- DEFAULT_NAMESPACE = "datashare-default"
12
-
13
- METADATA_JSON = "metadata.json"
14
- MANIFEST_JSON = "manifest.json"
15
-
16
- TIKA_METADATA_RESOURCENAME = "tika_metadata_resourcename"