thds.core 1.38.20250616192452__py3-none-any.whl → 1.38.20250617225213__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.
- thds/core/inspect.py +3 -2
- thds/core/meta.py +153 -14
- thds/core/sqlite/insert_utils.py +1 -1
- {thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/METADATA +1 -1
- {thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/RECORD +8 -8
- {thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/WHEEL +0 -0
- {thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/entry_points.txt +0 -0
- {thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/top_level.txt +0 -0
thds/core/inspect.py
CHANGED
thds/core/meta.py
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
"""Some parts of this module having to do with actual metadata files have been removed,
|
|
2
|
-
being no longer in use.
|
|
3
|
-
|
|
4
|
-
The actual removal was done mainly to enable us to remove attrs and cattrs as dependencies,
|
|
5
|
-
not because we don't like them, but because reducing our depedency footprint in
|
|
6
|
-
our most central library is a good idea.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
1
|
import importlib
|
|
2
|
+
import json
|
|
10
3
|
import os
|
|
11
4
|
import re
|
|
12
5
|
import typing as ty
|
|
13
|
-
from dataclasses import dataclass, field
|
|
14
6
|
from datetime import datetime, timezone
|
|
15
7
|
from functools import lru_cache
|
|
16
8
|
from getpass import getuser
|
|
17
9
|
from importlib.metadata import PackageNotFoundError, version
|
|
18
|
-
from importlib.resources import Package
|
|
10
|
+
from importlib.resources import Package, open_text
|
|
19
11
|
from pathlib import Path
|
|
20
12
|
from types import MappingProxyType
|
|
21
13
|
|
|
14
|
+
import attrs
|
|
15
|
+
from cattrs import Converter
|
|
16
|
+
|
|
22
17
|
from . import calgitver, git
|
|
23
18
|
from .log import getLogger
|
|
19
|
+
from .types import StrOrPath
|
|
24
20
|
|
|
25
21
|
LayoutType = ty.Literal["flat", "src"]
|
|
26
22
|
NameFormatType = ty.Literal["git", "docker", "hive"]
|
|
@@ -41,6 +37,8 @@ GIT_IS_DIRTY = "GIT_IS_DIRTY"
|
|
|
41
37
|
GIT_BRANCH = "GIT_BRANCH"
|
|
42
38
|
THDS_USER = "THDS_USER"
|
|
43
39
|
|
|
40
|
+
META_FILE = "meta.json"
|
|
41
|
+
|
|
44
42
|
LOGGER = getLogger(__name__)
|
|
45
43
|
|
|
46
44
|
|
|
@@ -172,6 +170,12 @@ def get_version(pkg: Package, orig: str = "") -> str:
|
|
|
172
170
|
# 'recurse' upward, assuming that the package name is overly-specified
|
|
173
171
|
pkg_ = pkg.split(".")
|
|
174
172
|
if len(pkg_) <= 1:
|
|
173
|
+
# Check to see if there's a
|
|
174
|
+
# meta.json file hanging around, and if so, see if it contains a pyproject_version.
|
|
175
|
+
metadata = read_metadata(orig or pkg)
|
|
176
|
+
if metadata and metadata.pyproject_version:
|
|
177
|
+
return metadata.pyproject_version
|
|
178
|
+
|
|
175
179
|
for env_var in ("CALGITVER", "GIT_COMMIT"):
|
|
176
180
|
env_var_version = os.getenv(env_var)
|
|
177
181
|
lvl = LOGGER.debug if env_var == "CALGITVER" else LOGGER.info
|
|
@@ -236,6 +240,16 @@ def get_commit(pkg: Package = "") -> str: # should really be named get_commit_h
|
|
|
236
240
|
except git.NO_GIT:
|
|
237
241
|
pass
|
|
238
242
|
|
|
243
|
+
try:
|
|
244
|
+
if pkg:
|
|
245
|
+
LOGGER.debug("`get_commit` reading from metadata.")
|
|
246
|
+
metadata = read_metadata(pkg)
|
|
247
|
+
if metadata.is_empty:
|
|
248
|
+
raise EmptyMetadataException
|
|
249
|
+
return metadata.git_commit
|
|
250
|
+
except EmptyMetadataException:
|
|
251
|
+
pass
|
|
252
|
+
|
|
239
253
|
LOGGER.warning("`get_commit` found no commit.")
|
|
240
254
|
return ""
|
|
241
255
|
|
|
@@ -255,6 +269,16 @@ def is_clean(pkg: Package = "") -> bool:
|
|
|
255
269
|
except git.NO_GIT:
|
|
256
270
|
pass
|
|
257
271
|
|
|
272
|
+
try:
|
|
273
|
+
if pkg:
|
|
274
|
+
LOGGER.debug("`is_clean` reading from metadata.")
|
|
275
|
+
metadata = read_metadata(pkg)
|
|
276
|
+
if metadata.is_empty:
|
|
277
|
+
raise EmptyMetadataException
|
|
278
|
+
return metadata.git_is_clean
|
|
279
|
+
except EmptyMetadataException:
|
|
280
|
+
pass
|
|
281
|
+
|
|
258
282
|
LOGGER.warning("`is_clean` found no cleanliness - assume dirty.")
|
|
259
283
|
return False
|
|
260
284
|
|
|
@@ -270,6 +294,16 @@ def get_branch(pkg: Package = "", format: NameFormatType = "git") -> str:
|
|
|
270
294
|
except git.NO_GIT:
|
|
271
295
|
pass
|
|
272
296
|
|
|
297
|
+
try:
|
|
298
|
+
if pkg:
|
|
299
|
+
LOGGER.debug("`get_branch` reading from metadata.")
|
|
300
|
+
metadata = read_metadata(pkg)
|
|
301
|
+
if not metadata.git_branch:
|
|
302
|
+
raise EmptyMetadataException
|
|
303
|
+
return metadata.git_branch
|
|
304
|
+
except EmptyMetadataException:
|
|
305
|
+
pass
|
|
306
|
+
|
|
273
307
|
LOGGER.warning("`get_branch` found no branch.")
|
|
274
308
|
return ""
|
|
275
309
|
|
|
@@ -282,12 +316,27 @@ def get_user(pkg: Package = "", format: NameFormatType = "git") -> str:
|
|
|
282
316
|
LOGGER.debug("`get_user` reading from env var.")
|
|
283
317
|
return os.environ[THDS_USER]
|
|
284
318
|
|
|
319
|
+
try:
|
|
320
|
+
if pkg:
|
|
321
|
+
LOGGER.debug("`get_user` reading from metadata.")
|
|
322
|
+
metadata = read_metadata(pkg)
|
|
323
|
+
if not metadata.thds_user:
|
|
324
|
+
raise EmptyMetadataException
|
|
325
|
+
return metadata.thds_user
|
|
326
|
+
except EmptyMetadataException:
|
|
327
|
+
pass
|
|
328
|
+
|
|
285
329
|
LOGGER.debug("`get_user` found no user data - getting system user.")
|
|
286
330
|
return getuser()
|
|
287
331
|
|
|
288
332
|
return format_name(_get_user(pkg), format)
|
|
289
333
|
|
|
290
334
|
|
|
335
|
+
def is_deployed(pkg: Package) -> bool:
|
|
336
|
+
meta = read_metadata(pkg)
|
|
337
|
+
return not meta.is_empty
|
|
338
|
+
|
|
339
|
+
|
|
291
340
|
def _hacky_get_pyproject_toml_version(pkg: Package, wdir: Path) -> str:
|
|
292
341
|
# it will be a good day when Python packages a toml reader by default.
|
|
293
342
|
ppt = wdir / "pyproject.toml"
|
|
@@ -330,14 +379,14 @@ def find_pyproject_toml_version(starting_path: Path, pkg: Package) -> str:
|
|
|
330
379
|
MiscType = ty.Mapping[str, ty.Union[str, int, float, bool]]
|
|
331
380
|
|
|
332
381
|
|
|
333
|
-
@
|
|
382
|
+
@attrs.frozen
|
|
334
383
|
class Metadata:
|
|
335
384
|
git_commit: str = ""
|
|
336
385
|
git_branch: str = ""
|
|
337
386
|
git_is_clean: bool = False
|
|
338
387
|
pyproject_version: str = "" # only present if the project defines `version` inside pyproject.toml
|
|
339
388
|
thds_user: str = ""
|
|
340
|
-
misc: MiscType = field(
|
|
389
|
+
misc: MiscType = attrs.field(factory=lambda: MappingProxyType(dict()))
|
|
341
390
|
|
|
342
391
|
@property
|
|
343
392
|
def docker_branch(self) -> str:
|
|
@@ -357,13 +406,103 @@ class Metadata:
|
|
|
357
406
|
|
|
358
407
|
@property
|
|
359
408
|
def is_empty(self) -> bool:
|
|
360
|
-
return all(not getattr(self,
|
|
409
|
+
return all(not getattr(self, field.name) for field in attrs.fields(Metadata))
|
|
361
410
|
|
|
362
411
|
@property
|
|
363
412
|
def git_is_dirty(self) -> bool:
|
|
364
413
|
return not self.git_is_clean
|
|
365
414
|
|
|
366
415
|
|
|
416
|
+
meta_converter = Converter(forbid_extra_keys=True)
|
|
417
|
+
meta_converter.register_structure_hook(
|
|
418
|
+
Metadata, lambda v, _: Metadata(misc=MappingProxyType(v.pop("misc", {})), **v)
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class EmptyMetadataException(Exception):
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def init_metadata(misc: ty.Optional[MiscType] = None, pyproject_toml_version: str = "") -> Metadata:
|
|
427
|
+
return Metadata(
|
|
428
|
+
git_commit=get_commit(),
|
|
429
|
+
git_branch=get_branch(),
|
|
430
|
+
git_is_clean=is_clean(),
|
|
431
|
+
pyproject_version=pyproject_toml_version,
|
|
432
|
+
thds_user=os.getenv(THDS_USER, getuser()),
|
|
433
|
+
misc=MappingProxyType(misc) if misc else MappingProxyType(dict()),
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _sanitize_metadata_for_docker_tools(d: dict):
|
|
438
|
+
"""We want our Docker builds to be able to take advantage of
|
|
439
|
+
caching based on the contents of the sources copied over into
|
|
440
|
+
them. If we embed a meta.json into each library where the commit
|
|
441
|
+
hash changes every time a commit happens, then we've blown away
|
|
442
|
+
our entire cache.
|
|
443
|
+
|
|
444
|
+
The Docker builds already inject this metadata as environment
|
|
445
|
+
variables after the source copies happen, so there's no need for
|
|
446
|
+
us to embed it this way.
|
|
447
|
+
"""
|
|
448
|
+
d["git_commit"] = ""
|
|
449
|
+
d["git_branch"] = ""
|
|
450
|
+
d["git_is_clean"] = ""
|
|
451
|
+
d["thds_user"] = THDS_USER
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def write_metadata(
|
|
455
|
+
pkg: str,
|
|
456
|
+
*,
|
|
457
|
+
misc: ty.Optional[MiscType] = None,
|
|
458
|
+
namespace: str = "thds",
|
|
459
|
+
layout: LayoutType = "src",
|
|
460
|
+
wdir: ty.Optional[StrOrPath] = None,
|
|
461
|
+
deploying: bool = False,
|
|
462
|
+
for_docker_tools_build: bool = False,
|
|
463
|
+
) -> None:
|
|
464
|
+
wdir_ = Path(wdir) if wdir else Path(".")
|
|
465
|
+
assert wdir_
|
|
466
|
+
if os.getenv(DEPLOYING) or deploying:
|
|
467
|
+
LOGGER.debug("Writing metadata.")
|
|
468
|
+
metadata = init_metadata(
|
|
469
|
+
misc=misc, pyproject_toml_version=_hacky_get_pyproject_toml_version(pkg, wdir_)
|
|
470
|
+
)
|
|
471
|
+
metadata_path = os.path.join(
|
|
472
|
+
"src" if layout == "src" else "",
|
|
473
|
+
namespace.replace("-", "/").replace(".", "/"),
|
|
474
|
+
pkg.replace("-", "_").replace(".", "/"),
|
|
475
|
+
META_FILE,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
LOGGER.info(f"Writing metadata for {pkg} to {wdir_ / metadata_path}")
|
|
479
|
+
with open(wdir_ / metadata_path, "w") as f:
|
|
480
|
+
metadata_dict = meta_converter.unstructure(metadata)
|
|
481
|
+
if for_docker_tools_build:
|
|
482
|
+
_sanitize_metadata_for_docker_tools(metadata_dict)
|
|
483
|
+
json.dump(metadata_dict, f, indent=2)
|
|
484
|
+
f.write("\n") # Add newline because Py JSON does not
|
|
485
|
+
|
|
486
|
+
|
|
367
487
|
@lru_cache(None)
|
|
368
488
|
def read_metadata(pkg: Package) -> Metadata:
|
|
369
|
-
|
|
489
|
+
LOGGER.debug("Reading metadata.")
|
|
490
|
+
|
|
491
|
+
if pkg == "__main__":
|
|
492
|
+
raise ValueError("`read_meta` expects a package or module name, not '__main__'.")
|
|
493
|
+
|
|
494
|
+
if not pkg:
|
|
495
|
+
raise ValueError(
|
|
496
|
+
"`read_meta` is missing a package or module name. "
|
|
497
|
+
"If using `__package__` make sure an __init__.py is present."
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
with open_text(pkg, META_FILE) as f:
|
|
502
|
+
return meta_converter.structure(json.load(f), Metadata)
|
|
503
|
+
# pkg=__name__ will raise a TypeError unless it is called in an __init__.py
|
|
504
|
+
except (ModuleNotFoundError, FileNotFoundError, TypeError):
|
|
505
|
+
pkg_ = pkg.split(".")
|
|
506
|
+
if len(pkg_) <= 1:
|
|
507
|
+
return Metadata()
|
|
508
|
+
return read_metadata(".".join(pkg_[:-1]))
|
thds/core/sqlite/insert_utils.py
CHANGED
|
@@ -19,13 +19,13 @@ thds/core/hashing.py,sha256=OqaV65vGKpT3l78jm-Uh7xG4DtAczGjk9-Q60OGmhY0,3521
|
|
|
19
19
|
thds/core/home.py,sha256=tTClL_AarIKeri1aNCpuIC6evD7qr83ESGD173B81hU,470
|
|
20
20
|
thds/core/hostname.py,sha256=canFGr-JaaG7nUfsQlyL0JT-2tnZoT1BvXzyaOMK1vA,208
|
|
21
21
|
thds/core/imports.py,sha256=0LVegY8I8_XKZPcqiIp2OVVzEDtyqYA3JETf9OAKNKs,568
|
|
22
|
-
thds/core/inspect.py,sha256=
|
|
22
|
+
thds/core/inspect.py,sha256=vCxKqw8XG2W1cuj0MwjdXhe9TLQrGdjRraS6UEYsbf8,1955
|
|
23
23
|
thds/core/iterators.py,sha256=d3iTQDR0gCW1nMRmknQeodR_4THzR9Ajmp8F8KCCFgg,208
|
|
24
24
|
thds/core/lazy.py,sha256=e1WvG4LsbEydV0igEr_Vl1cq05zlQNIE8MFYT90yglE,3289
|
|
25
25
|
thds/core/link.py,sha256=kmFJIFvEZc16-7S7IGvtTpzwl3VuvFl3yPlE6WJJ03w,5404
|
|
26
26
|
thds/core/logical_root.py,sha256=gWkIYRv9kNQfzbpxJaYiwNXVz1neZ2NvnvProtOn9d8,1399
|
|
27
27
|
thds/core/merge_args.py,sha256=7oj7dtO1-XVkfTM3aBlq3QlZbo8tb6X7E3EVIR-60t8,5781
|
|
28
|
-
thds/core/meta.py,sha256=
|
|
28
|
+
thds/core/meta.py,sha256=5um8Gvl00JFrYdpYfK2qD3pyQEoq-_3T2LXAhFOcTNo,16617
|
|
29
29
|
thds/core/parallel.py,sha256=HXAn9aIYqNE5rnRN5ypxR6CUucdfzE5T5rJ_MUv-pFk,7590
|
|
30
30
|
thds/core/pickle_visit.py,sha256=QNMWIi5buvk2zsvx1-D-FKL7tkrFUFDs387vxgGebgU,833
|
|
31
31
|
thds/core/prof.py,sha256=5ViolfPsAPwUTHuhAe-bon7IArPGXydpGoB5uZmObDk,8264
|
|
@@ -59,7 +59,7 @@ thds/core/sqlite/copy.py,sha256=y3IRQTBrWDfKuVIfW7fYuEgwRCRKHjN0rxVFkIb9VrQ,1155
|
|
|
59
59
|
thds/core/sqlite/ddl.py,sha256=k9BvmDzb0rrlhmEpXkB6ESaZAUWtbL58x-70sPyoFk4,201
|
|
60
60
|
thds/core/sqlite/functions.py,sha256=AOIRzb7lNxmFm1J5JS6R8Nl-dSv3Dy47UNZVVjl1rvk,2158
|
|
61
61
|
thds/core/sqlite/index.py,sha256=Vc7qxPqQ69A6GO5gmVQf5e3y8f8IqOTHgyEDoVZxTFM,903
|
|
62
|
-
thds/core/sqlite/insert_utils.py,sha256=
|
|
62
|
+
thds/core/sqlite/insert_utils.py,sha256=LUVcznl-xCVoh_L_6tabVYUAYnEnaVDmBX2PeopLMKU,884
|
|
63
63
|
thds/core/sqlite/merge.py,sha256=NxettDMJ_mcrWfteQn_ERY7MUB5ETR-yJLKg7uvF6zA,3779
|
|
64
64
|
thds/core/sqlite/meta.py,sha256=4P65PAmCjagHYO1Z6nWM-wkjEWv3hxw5qVa4cIpcH_8,5859
|
|
65
65
|
thds/core/sqlite/read.py,sha256=5pWvrbed3XNWgSy-79-8ONWkkt4jWbTzFNW6SnOrdYQ,2576
|
|
@@ -68,8 +68,8 @@ thds/core/sqlite/structured.py,sha256=SvZ67KcVcVdmpR52JSd52vMTW2ALUXmlHEeD-VrzWV
|
|
|
68
68
|
thds/core/sqlite/types.py,sha256=oUkfoKRYNGDPZRk29s09rc9ha3SCk2SKr_K6WKebBFs,1308
|
|
69
69
|
thds/core/sqlite/upsert.py,sha256=BmKK6fsGVedt43iY-Lp7dnAu8aJ1e9CYlPVEQR2pMj4,5827
|
|
70
70
|
thds/core/sqlite/write.py,sha256=z0219vDkQDCnsV0WLvsj94keItr7H4j7Y_evbcoBrWU,3458
|
|
71
|
-
thds_core-1.38.
|
|
72
|
-
thds_core-1.38.
|
|
73
|
-
thds_core-1.38.
|
|
74
|
-
thds_core-1.38.
|
|
75
|
-
thds_core-1.38.
|
|
71
|
+
thds_core-1.38.20250617225213.dist-info/METADATA,sha256=eD4VIWf_BXHdJ6N8MOR5f4smatXcSKdZ6MHzRDDOLjs,2216
|
|
72
|
+
thds_core-1.38.20250617225213.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
73
|
+
thds_core-1.38.20250617225213.dist-info/entry_points.txt,sha256=bOCOVhKZv7azF3FvaWX6uxE6yrjK6FcjqhtxXvLiFY8,161
|
|
74
|
+
thds_core-1.38.20250617225213.dist-info/top_level.txt,sha256=LTZaE5SkWJwv9bwOlMbIhiS-JWQEEIcjVYnJrt-CriY,5
|
|
75
|
+
thds_core-1.38.20250617225213.dist-info/RECORD,,
|
|
File without changes
|
{thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{thds_core-1.38.20250616192452.dist-info → thds_core-1.38.20250617225213.dist-info}/top_level.txt
RENAMED
|
File without changes
|