3
3
import re
4
4
from dataclasses import dataclass
5
5
from pathlib import Path
6
- from typing import Any , Dict , Iterable , List , Optional , Protocol , Tuple , Type , TypeVar
6
+ from typing import (
7
+ Any ,
8
+ Dict ,
9
+ Iterable ,
10
+ List ,
11
+ Optional ,
12
+ Protocol ,
13
+ Tuple ,
14
+ Type ,
15
+ TypeVar ,
16
+ Union ,
17
+ )
7
18
8
19
from pip ._vendor import tomli_w
9
20
from pip ._vendor .packaging .version import InvalidVersion , Version
@@ -32,8 +43,20 @@ def is_valid_pylock_file_name(path: Path) -> bool:
32
43
return path .name == "pylock.toml" or bool (re .match (PYLOCK_FILE_NAME_RE , path .name ))
33
44
34
45
46
+ def _toml_key (key : str ) -> str :
47
+ return key .replace ("_" , "-" )
48
+
49
+
50
+ def _toml_value (value : T ) -> Union [str , T ]:
51
+ if isinstance (value , Version ):
52
+ return str (value )
53
+ return value
54
+
55
+
35
56
def _toml_dict_factory (data : List [Tuple [str , Any ]]) -> Dict [str , Any ]:
36
- return {key .replace ("_" , "-" ): value for key , value in data if value is not None }
57
+ return {
58
+ _toml_key (key ): _toml_value (value ) for key , value in data if value is not None
59
+ }
37
60
38
61
39
62
def _get (
@@ -58,6 +81,23 @@ def _get_required(d: Dict[str, Any], expected_type: Type[T], key: str) -> T:
58
81
return value
59
82
60
83
84
+ def _get_version (d : Dict [str , Any ], key : str ) -> Optional [Version ]:
85
+ value = _get (d , str , key )
86
+ if value is None :
87
+ return None
88
+ try :
89
+ return Version (value )
90
+ except InvalidVersion :
91
+ raise PylockUnsupportedVersionError (f"invalid version { value !r} " )
92
+
93
+
94
+ def _get_required_version (d : Dict [str , Any ], key : str ) -> Version :
95
+ value = _get_version (d , key )
96
+ if value is None :
97
+ raise PylockRequiredKeyError (key )
98
+ return value
99
+
100
+
61
101
def _get_object (
62
102
d : Dict [str , Any ], expected_type : Type [PylockDataClassT ], key : str
63
103
) -> Optional [PylockDataClassT ]:
@@ -233,7 +273,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Self:
233
273
@dataclass
234
274
class Package :
235
275
name : str
236
- version : Optional [str ] = None
276
+ version : Optional [Version ] = None
237
277
# (not supported) marker: Optional[str]
238
278
# (not supported) requires_python: Optional[str]
239
279
# (not supported) dependencies
@@ -255,7 +295,7 @@ def __post_init__(self) -> None:
255
295
def from_dict (cls , d : Dict [str , Any ]) -> Self :
256
296
package = cls (
257
297
name = _get_required (d , str , "name" ),
258
- version = _get ( d , str , "version" ),
298
+ version = _get_version ( d , "version" ),
259
299
vcs = _get_object (d , PackageVcs , "vcs" ),
260
300
directory = _get_object (d , PackageDirectory , "directory" ),
261
301
archive = _get_object (d , PackageArchive , "archive" ),
@@ -314,7 +354,7 @@ def from_install_requirement(cls, ireq: InstallRequirement, base_dir: Path) -> S
314
354
# should never happen
315
355
raise NotImplementedError ()
316
356
else :
317
- package_version = str ( dist .version )
357
+ package_version = dist .version
318
358
if isinstance (download_info .info , ArchiveInfo ):
319
359
if not download_info .info .hashes :
320
360
raise NotImplementedError ()
@@ -351,7 +391,7 @@ def from_install_requirement(cls, ireq: InstallRequirement, base_dir: Path) -> S
351
391
352
392
@dataclass
353
393
class Pylock :
354
- lock_version : str = "1.0"
394
+ lock_version : Version = Version ( "1.0" )
355
395
# (not supported) environments: Optional[List[str]]
356
396
# (not supported) requires_python: Optional[str]
357
397
# (not supported) extras: List[str] = []
@@ -360,24 +400,15 @@ class Pylock:
360
400
packages : List [Package ] = dataclasses .field (default_factory = list )
361
401
# (not supported) tool: Optional[Dict[str, Any]]
362
402
363
- def _validate_version (self ) -> None :
364
- if not self .lock_version :
365
- raise PylockRequiredKeyError ("lock-version" )
366
- try :
367
- lock_version = Version (self .lock_version )
368
- except InvalidVersion :
403
+ def __post_init__ (self ) -> None :
404
+ if self .lock_version < Version ("1" ) or self .lock_version >= Version ("2" ):
369
405
raise PylockUnsupportedVersionError (
370
- f"invalid pylock version { self .lock_version !r } "
406
+ f"pylock version { self .lock_version } is not supported "
371
407
)
372
- if lock_version < Version ( "1" ) or lock_version >= Version ("2 " ):
373
- raise PylockUnsupportedVersionError (
374
- f "pylock version { lock_version } is not supported"
408
+ if self . lock_version > Version ("1.0 " ):
409
+ logging . warning (
410
+ "pylock minor version %s is not supported" , self . lock_version
375
411
)
376
- if lock_version > Version ("1.0" ):
377
- logging .warning ("pylock minor version %s is not supported" , lock_version )
378
-
379
- def __post_init__ (self ) -> None :
380
- self ._validate_version ()
381
412
382
413
def as_toml (self ) -> str :
383
414
return tomli_w .dumps (self .to_dict ())
@@ -388,7 +419,7 @@ def to_dict(self) -> Dict[str, Any]:
388
419
@classmethod
389
420
def from_dict (cls , d : Dict [str , Any ]) -> Self :
390
421
return cls (
391
- lock_version = _get_required ( d , str , "lock-version" ),
422
+ lock_version = _get_required_version ( d , "lock-version" ),
392
423
created_by = _get_required (d , str , "created-by" ),
393
424
packages = _get_required_list_of_objects (d , Package , "packages" ),
394
425
)
0 commit comments