33JSON Schema library).
44"""
55
6+ import collections
7+ import itertools
68from inspect import cleandoc
7- from typing import Mapping , TypeVar
9+ from typing import Generator , Iterable , Mapping , TypeVar
810
911from .error_reporting import ValidationError
1012
@@ -30,6 +32,24 @@ class IncludedDependencyGroupMustExist(ValidationError):
3032 _URL = "https://peps.python.org/pep-0735/"
3133
3234
35+ class ImportNameCollision (ValidationError ):
36+ _DESC = """According to PEP 794:
37+
38+ All import-names and import-namespaces items must be unique.
39+ """
40+ __doc__ = _DESC
41+ _URL = "https://peps.python.org/pep-0794/"
42+
43+
44+ class ImportNameMissing (ValidationError ):
45+ _DESC = """According to PEP 794:
46+
47+ An import name must have all parents listed.
48+ """
49+ __doc__ = _DESC
50+ _URL = "https://peps.python.org/pep-0794/"
51+
52+
3353def validate_project_dynamic (pyproject : T ) -> T :
3454 project_table = pyproject .get ("project" , {})
3555 dynamic = project_table .get ("dynamic" , [])
@@ -78,4 +98,54 @@ def validate_include_depenency(pyproject: T) -> T:
7898 return pyproject
7999
80100
81- EXTRA_VALIDATIONS = (validate_project_dynamic , validate_include_depenency )
101+ def _remove_private (items : Iterable [str ]) -> Generator [str , None , None ]:
102+ for item in items :
103+ yield item .partition (";" )[0 ].rstrip ()
104+
105+
106+ def validate_import_name_issues (pyproject : T ) -> T :
107+ project = pyproject .get ("project" , {})
108+ import_names = collections .Counter (_remove_private (project .get ("import-names" , [])))
109+ import_namespaces = collections .Counter (
110+ _remove_private (project .get ("import-namespaces" , []))
111+ )
112+
113+ duplicated = [k for k , v in (import_names + import_namespaces ).items () if v > 1 ]
114+
115+ if duplicated :
116+ raise ImportNameCollision (
117+ message = "Duplicated names are not allowed in import-names/import-namespaces" ,
118+ value = duplicated ,
119+ name = "data.project.importnames(paces)" ,
120+ definition = {
121+ "description" : cleandoc (ImportNameCollision ._DESC ),
122+ "see" : ImportNameCollision ._URL ,
123+ },
124+ rule = "PEP 794" ,
125+ )
126+
127+ names = frozenset (import_names + import_namespaces )
128+ for name in names :
129+ for parent in itertools .accumulate (
130+ name .split ("." )[:- 1 ], lambda a , b : f"{ a } .{ b } "
131+ ):
132+ if parent not in names :
133+ raise ImportNameMissing (
134+ message = "All parents of an import name must also be listed in import-namespace/import-names" ,
135+ value = name ,
136+ name = "data.project.importnames(paces)" ,
137+ definition = {
138+ "description" : cleandoc (ImportNameMissing ._DESC ),
139+ "see" : ImportNameMissing ._URL ,
140+ },
141+ rule = "PEP 794" ,
142+ )
143+
144+ return pyproject
145+
146+
147+ EXTRA_VALIDATIONS = (
148+ validate_project_dynamic ,
149+ validate_include_depenency ,
150+ validate_import_name_issues ,
151+ )
0 commit comments