11from __future__ import annotations
22
3- from typing import Iterable
3+ from typing import (
4+ Iterable ,
5+ List ,
6+ Dict
7+ )
48
59import nox
610from nox import Session
711
812from exasol .toolbox .nox ._shared import python_files
913from noxconfig import PROJECT_CONFIG
1014
15+ from pathlib import Path
16+ import rich .console
17+ import tomlkit
18+ import sys
19+
1120
1221def _pylint (session : Session , files : Iterable [str ]) -> None :
1322 session .run (
@@ -65,6 +74,61 @@ def _security_lint(session: Session, files: Iterable[str]) -> None:
6574 )
6675
6776
77+ class Dependencies :
78+ def __init__ (self , illegal : Dict [str , List [str ]] | None ):
79+ self ._illegal = illegal or {}
80+
81+ @staticmethod
82+ def parse (pyproject_toml : str ) -> "Dependencies" :
83+ def _source_filter (version ) -> bool :
84+ ILLEGAL_SPECIFIERS = ['url' , 'git' , 'path' ]
85+ return any (
86+ specifier in version
87+ for specifier in ILLEGAL_SPECIFIERS
88+ )
89+
90+ def find_illegal (part ) -> List [str ]:
91+ return [
92+ f"{ name } = { version } "
93+ for name , version in part .items ()
94+ if _source_filter (version )
95+ ]
96+
97+ illegal : Dict [str , List [str ]] = {}
98+ toml = tomlkit .loads (pyproject_toml )
99+ poetry = toml .get ("tool" , {}).get ("poetry" , {})
100+
101+ part = poetry .get ("dependencies" , {})
102+ if illegal_group := find_illegal (part ):
103+ illegal ["tool.poetry.dependencies" ] = illegal_group
104+
105+ part = poetry .get ("dev" , {}).get ("dependencies" , {})
106+ if illegal_group := find_illegal (part ):
107+ illegal ["tool.poetry.dev.dependencies" ] = illegal_group
108+
109+ part = poetry .get ("group" , {})
110+ for group , content in part .items ():
111+ illegal_group = find_illegal (content .get ("dependencies" , {}))
112+ if illegal_group :
113+ illegal [f"tool.poetry.group.{ group } .dependencies" ] = illegal_group
114+ return Dependencies (illegal )
115+
116+ @property
117+ def illegal (self ) -> Dict [str , List [str ]]:
118+ return self ._illegal
119+
120+
121+ def report_illegal (illegal : Dict [str , List [str ]], console : rich .console .Console ):
122+ count = sum (len (deps ) for deps in illegal .values ())
123+ suffix = "y" if count == 1 else "ies"
124+ console .print (f"{ count } illegal dependenc{ suffix } \n " , style = "red" )
125+ for section , dependencies in illegal .items ():
126+ console .print (f"\\ [{ section } ]" , style = "red" )
127+ for dependency in dependencies :
128+ console .print (dependency , style = "red" )
129+ console .print ("" )
130+
131+
68132@nox .session (name = "lint:code" , python = False )
69133def lint (session : Session ) -> None :
70134 "Runs the static code analyzer on the project"
@@ -84,3 +148,14 @@ def security_lint(session: Session) -> None:
84148 """Runs the security linter on the project"""
85149 py_files = [f"{ file } " for file in python_files (PROJECT_CONFIG .root )]
86150 _security_lint (session , list (filter (lambda file : "test" not in file , py_files )))
151+
152+
153+ @nox .session (name = "lint:dependencies" , python = False )
154+ def dependency_check (session : Session ) -> None :
155+ """Checks if only valid sources of dependencies are used"""
156+ content = Path (PROJECT_CONFIG .root , "pyproject.toml" ).read_text ()
157+ dependencies = Dependencies .parse (content )
158+ console = rich .console .Console ()
159+ if illegal := dependencies .illegal :
160+ report_illegal (illegal , console )
161+ sys .exit (1 )
0 commit comments