@@ -21,96 +21,73 @@ class Versions(TypedDict, total=True):
2121
2222
2323@pytest .fixture
24- def project_root (request : pytest .FixtureRequest ):
25- return request .config .rootpath
26-
27-
28- class TestFacades :
29- def test_client_facades (self , project_root : Path ) -> None :
30- good_facades = self .generate_client_facades (project_root )
31- client_facades = cast (ClientFacades , connection .client_facades )
32-
33- errors : List [Tuple [str , Optional [List [int ]], Optional [List [int ]]]] = []
34- all_names = sorted (set (connection .client_facades ).union (good_facades ))
35- for name in all_names :
36- expected = self ._versions_from_facades (good_facades , name )
37- actual = self ._versions_from_facades (client_facades , name )
38- if expected != actual :
39- errors .append ((name , expected , actual ))
40-
41- if errors :
42- print ('The following errors were found in connection.client_facades:' )
43- for name , expected , actual in errors :
44- expected_msg = (
45- f'should be { expected } ,'
46- if expected is not None
47- else 'should not be present,'
48- )
49- actual_msg = (
50- f'not { actual } '
51- if actual is not None
52- else 'but is not present'
53- )
54- print (f' { name !r} { expected_msg } { actual_msg } ' )
55-
56- assert not errors
57-
58- @classmethod
59- def generate_client_facades (cls , project_root : Path ) -> ClientFacades :
60- """Return a client_facades dictionary from generated code under project_root.
61- """
62- files_by_version : List [Tuple [int , Path ]] = []
63- # [(facade_version, Path), ...]
64- for file in (project_root / 'juju' / 'client' ).glob ('_client[0-9]*.py' ):
65- files_by_version .append ((cls ._version_from_filename (file ), file ))
66- files_by_version .sort ()
67-
68- # _clientN.py files import * from _definitions
69- # so we will ignore any names from there
70- ignore = dir (importlib .import_module ('juju.client._definitions' ))
71-
72- facades_by_version : Dict [int , Set [str ]] = {}
73- # {facade_version: {facade_name, ...}, ...}
74- for version , file in files_by_version :
75- module = cls ._try_import (f'juju.client.{ file .stem } ' )
76- facades = {
77- name .removesuffix ("Facade" )
78- for name in dir (module )
79- if not (name .startswith ('_' ) or name in ignore )
80- }
81- facades_by_version [version ] = facades
82-
83- # client_facades in connection.py is sorted
84- # so we sort facade names before constructing it
85- first , * rest = facades_by_version .values ()
86- sorted_facade_names : list [str ] = sorted (first .union (* rest ))
87-
88- client_facades : ClientFacades = {}
89- # {facade_name: {'versions': [1, 2, 3, ...]}, ...}
90- for name in sorted_facade_names :
91- versions : List [int ] = []
92- for version , facades in facades_by_version .items ():
93- if name in facades :
94- versions .append (version )
95- client_facades [name ] = {'versions' : versions }
96- return client_facades
97-
98- @staticmethod
99- def _try_import (module_name : str ) -> Optional [ModuleType ]:
100- try :
101- return importlib .import_module (module_name )
102- except NameError as e :
103- warnings .warn (f'error on importing { module_name } :\n { type (e ).__name__ } : { e } ' )
104- return None
105-
106- @staticmethod
107- def _version_from_filename (path : Path ) -> int :
108- match = re .search ('_client([0-9]+).py' , path .name )
109- assert match
110- return int (match .group (1 ))
111-
112- @staticmethod
113- def _versions_from_facades (facades : ClientFacades , name : str ) -> Optional [List [int ]]:
114- if name not in facades :
115- return None
116- return facades [name ]['versions' ]
24+ def project_root (pytestconfig : pytest .Config ) -> Path :
25+ return pytestconfig .rootpath
26+
27+
28+ def test_client_facades (project_root : Path ) -> None :
29+ good_facades = make_client_facades_from_generated_code (project_root )
30+ client_facades = cast (ClientFacades , connection .client_facades )
31+
32+ assert {
33+ k : v ['versions' ] for k , v in client_facades .items ()
34+ } == {
35+ k : v ['versions' ] for k , v in good_facades .items ()
36+ }
37+
38+
39+ def make_client_facades_from_generated_code (project_root : Path ) -> ClientFacades :
40+ """Return a client_facades dictionary from generated code under project_root.
41+ """
42+ files_by_version : List [Tuple [int , Path ]] = []
43+ # [(facade_version, Path), ...]
44+ for file in (project_root / 'juju' / 'client' ).glob ('_client[0-9]*.py' ):
45+ files_by_version .append ((_version_from_filename (file ), file ))
46+ files_by_version .sort ()
47+
48+ # _clientN.py files import * from _definitions
49+ # so we will ignore any names from there
50+ ignore = dir (importlib .import_module ('juju.client._definitions' ))
51+
52+ facades_by_version : Dict [int , Set [str ]] = {}
53+ # {facade_version: {facade_name, ...}, ...}
54+ for version , file in files_by_version :
55+ module = _try_import (f'juju.client.{ file .stem } ' )
56+ facades = {
57+ name .removesuffix ("Facade" )
58+ for name in dir (module )
59+ if not (name .startswith ('_' ) or name in ignore )
60+ }
61+ facades_by_version [version ] = facades
62+
63+ # client_facades in connection.py is sorted
64+ # so we sort facade names before constructing it
65+ first , * rest = facades_by_version .values ()
66+ sorted_facade_names : list [str ] = sorted (first .union (* rest ))
67+
68+ client_facades : ClientFacades = {}
69+ # {facade_name: {'versions': [1, 2, 3, ...]}, ...}
70+ for name in sorted_facade_names :
71+ versions : List [int ] = []
72+ for version , facades in facades_by_version .items ():
73+ if name in facades :
74+ versions .append (version )
75+ client_facades [name ] = {'versions' : versions }
76+ return client_facades
77+
78+ def _try_import (module_name : str ) -> Optional [ModuleType ]:
79+ try :
80+ return importlib .import_module (module_name )
81+ except NameError as e :
82+ warnings .warn (f'error on importing { module_name } :\n { type (e ).__name__ } : { e } ' )
83+ return None
84+
85+ def _version_from_filename (path : Path ) -> int :
86+ match = re .search ('_client([0-9]+).py' , path .name )
87+ assert match
88+ return int (match .group (1 ))
89+
90+ def _versions_from_facades (facades : ClientFacades , name : str ) -> Optional [List [int ]]:
91+ if name not in facades :
92+ return None
93+ return facades [name ]['versions' ]
0 commit comments