11from __future__ import annotations
22
3- from typing import Iterable
3+ from typing import (
4+ Iterable ,
5+ List ,
6+ Dict
7+ )
48import argparse
59from pathlib import Path
610
1014from exasol .toolbox .nox ._shared import python_files
1115from noxconfig import PROJECT_CONFIG
1216
17+ from pathlib import Path
18+ import rich .console
19+ import tomlkit
20+ import sys
21+
1322
1423def _pylint (session : Session , files : Iterable [str ]) -> None :
1524 session .run (
@@ -67,6 +76,7 @@ def _security_lint(session: Session, files: Iterable[str]) -> None:
6776 )
6877
6978
79+
7080def _import_lint (session : Session , path : Path ) -> None :
7181 session .run (
7282 "poetry" ,
@@ -76,6 +86,60 @@ def _import_lint(session: Session, path: Path) -> None:
7686 path
7787 )
7888
89+ class Dependencies :
90+ def __init__ (self , illegal : Dict [str , List [str ]] | None ):
91+ self ._illegal = illegal or {}
92+
93+ @staticmethod
94+ def parse (pyproject_toml : str ) -> "Dependencies" :
95+ def _source_filter (version ) -> bool :
96+ ILLEGAL_SPECIFIERS = ['url' , 'git' , 'path' ]
97+ return any (
98+ specifier in version
99+ for specifier in ILLEGAL_SPECIFIERS
100+ )
101+
102+ def find_illegal (part ) -> List [str ]:
103+ return [
104+ f"{ name } = { version } "
105+ for name , version in part .items ()
106+ if _source_filter (version )
107+ ]
108+
109+ illegal : Dict [str , List [str ]] = {}
110+ toml = tomlkit .loads (pyproject_toml )
111+ poetry = toml .get ("tool" , {}).get ("poetry" , {})
112+
113+ part = poetry .get ("dependencies" , {})
114+ if illegal_group := find_illegal (part ):
115+ illegal ["tool.poetry.dependencies" ] = illegal_group
116+
117+ part = poetry .get ("dev" , {}).get ("dependencies" , {})
118+ if illegal_group := find_illegal (part ):
119+ illegal ["tool.poetry.dev.dependencies" ] = illegal_group
120+
121+ part = poetry .get ("group" , {})
122+ for group , content in part .items ():
123+ illegal_group = find_illegal (content .get ("dependencies" , {}))
124+ if illegal_group :
125+ illegal [f"tool.poetry.group.{ group } .dependencies" ] = illegal_group
126+ return Dependencies (illegal )
127+
128+ @property
129+ def illegal (self ) -> Dict [str , List [str ]]:
130+ return self ._illegal
131+
132+
133+ def report_illegal (illegal : Dict [str , List [str ]], console : rich .console .Console ):
134+ count = sum (len (deps ) for deps in illegal .values ())
135+ suffix = "y" if count == 1 else "ies"
136+ console .print (f"{ count } illegal dependenc{ suffix } \n " , style = "red" )
137+ for section , dependencies in illegal .items ():
138+ console .print (f"\\ [{ section } ]" , style = "red" )
139+ for dependency in dependencies :
140+ console .print (dependency , style = "red" )
141+ console .print ("" )
142+
79143
80144@nox .session (name = "lint:code" , python = False )
81145def lint (session : Session ) -> None :
@@ -98,6 +162,16 @@ def security_lint(session: Session) -> None:
98162 _security_lint (session , list (filter (lambda file : "test" not in file , py_files )))
99163
100164
165+ @nox .session (name = "lint:dependencies" , python = False )
166+ def dependency_check (session : Session ) -> None :
167+ """Checks if only valid sources of dependencies are used"""
168+ content = Path (PROJECT_CONFIG .root , "pyproject.toml" ).read_text ()
169+ dependencies = Dependencies .parse (content )
170+ console = rich .console .Console ()
171+ if illegal := dependencies .illegal :
172+ report_illegal (illegal , console )
173+ sys .exit (1 )
174+
101175@nox .session (name = "lint:import" , python = False )
102176def import_lint (session : Session ) -> None :
103177 """(experimental) Runs import linter on the project"""
@@ -124,5 +198,4 @@ def import_lint(session: Session) -> None:
124198 session .error (
125199 "Please make sure you have a configuration file for the importlinter"
126200 )
127- _import_lint (session = session , path = path )
128-
201+ _import_lint (session = session , path = path )
0 commit comments