3636
3737# 3rd party
3838import click
39+ import dom_toml
3940from consolekit .terminal_colours import Fore , resolve_color_default
4041from domdf_python_tools .paths import PathPlus , in_directory
4142from domdf_python_tools .typing import PathLike
42- from shippinglabel .requirements import read_requirements
43+ from shippinglabel .requirements import ComparableRequirement , read_requirements
4344
4445# this package
4546from dep_checker .config import AllowedUnused , ConfigReader , NameMapping , NamespacePackages
6667
6768reader = ConfigReader ("dep_checker" , default_factory = dict )
6869
69- NODEP = re .compile (r".*#\s*nodep.*" )
70+ NODEP = re .compile (r".*#\s*nodep.*" , flags = re .IGNORECASE )
71+ EXTRADEP = re .compile (r".*#\s*extra(?:[-_ ])?dep\s*:\s*([a-z-_][a-z0-9-_]+).*" , flags = re .IGNORECASE )
7072
7173_nt_types = Union [Type ["PassingRequirement" ], Type ["UnlistedRequirement" ], Type ["UnusedRequirement" ]]
7274
@@ -203,6 +205,8 @@ class DepChecker:
203205 :no-default name_mapping:
204206 :param namespace_packages: List of namespace packages, e.g. ``ruamel.yaml``.
205207 :no-default namespace_packages:
208+ :param optional_requirements: A mapping of extra names to sets of optional requirements.
209+ :no-default optional_requirements:
206210 """
207211
208212 def __init__ (
@@ -213,11 +217,13 @@ def __init__(
213217 allowed_unused : Optional [Iterable [str ]] = None ,
214218 name_mapping : Optional [Mapping [str , str ]] = None ,
215219 namespace_packages : Optional [Iterable [str ]] = None ,
220+ optional_requirements : Optional [Dict [str , Set [ComparableRequirement ]]] = None
216221 ):
217222
218223 self .pkg_name : str = str (pkg_name ).rstrip (r"\/" )
219224 self .requirements : Set [str ] = set ()
220225 self .allowed_unused : List [str ] = list (allowed_unused or ())
226+ self .optional_requirements : Dict [str , Set [ComparableRequirement ]] = dict (optional_requirements or {})
221227
222228 name_mapping = dict (name_mapping or {})
223229
@@ -268,6 +274,25 @@ def check(
268274 # Marked with "# nodep", so the user wants to ignore this
269275 continue
270276
277+ extra_dep_match = EXTRADEP .match (file_content .splitlines ()[lineno - 1 ])
278+
279+ if extra_dep_match :
280+ # Marked with "# extra-dep: <name>", so this is an optional requirement
281+ extra_name = extra_dep_match .group (1 )
282+ if extra_name in self .optional_requirements :
283+ if import_name in self .requirements :
284+ min_lineno = min ((imports [import_name ].get (filename , lineno ), lineno ))
285+ # TODO: flag in the stdout as an input
286+ imports [import_name ][filename ] = min_lineno
287+ continue
288+ else :
289+ yield UnknownExtra (name = import_name , lineno = lineno , filename = filename .as_posix ())
290+
291+ print (extra_name )
292+ print (self .optional_requirements [extra_name ])
293+ input (">>>" )
294+ continue
295+
271296 yield UnlistedRequirement (name = import_name , lineno = lineno , filename = filename .as_posix ())
272297
273298 for req_name in sorted (self .requirements ):
0 commit comments