44import importlib
55import re
66from pathlib import Path
7- from typing import Dict , List , TypedDict , cast
7+ from typing import Dict , List , Set , Tuple , TypedDict , cast
88
99import pytest
1010
@@ -23,52 +23,60 @@ def project_root(pytestconfig: pytest.Config) -> Path:
2323 return pytestconfig .rootpath
2424
2525
26- def test_facade_version_matches_filename (project_root : Path ) -> None :
27- ignore_names = dir (_definitions )
28- for file in (project_root / 'juju' / 'client' ).glob ('_client[0-9]*.py' ):
29- match = re .search ('_client([0-9]+).py' , file .name )
30- assert match
31- version = int (match .group (1 ))
32- module = importlib .import_module (f'juju.client.{ file .stem } ' )
33- for cls_name in dir (module ):
34- if cls_name .startswith ('_' ) or cls_name in ignore_names :
35- continue
36- assert getattr (module , cls_name ).version == version
37-
38-
39- def test_class_name_matches_facade_name (project_root : Path ) -> None :
40- ignore_names = dir (_definitions )
41- for file in (project_root / 'juju' / 'client' ).glob ('_client[0-9]*.py' ):
42- module = importlib .import_module (f'juju.client.{ file .stem } ' )
43- for cls_name in dir (module ):
44- if cls_name .startswith ('_' ) or cls_name in ignore_names :
45- continue
46- assert getattr (module , cls_name ).name == cls_name .removesuffix ('Facade' )
47-
48-
49- def test_client_facades_matches_generated_code (project_root : Path ) -> None :
26+ def test_client_facades (project_root : Path ) -> None :
5027 client_facades = cast (ClientFacades , connection .client_facades )
51- expected_facades = make_client_facades_from_generated_code (project_root )
28+ good_facades = make_client_facades_from_generated_code (project_root )
5229 assert {
5330 k : v ['versions' ] for k , v in client_facades .items ()
5431 } == {
55- k : v ['versions' ] for k , v in expected_facades .items ()
32+ k : v ['versions' ] for k , v in good_facades .items ()
5633 }
5734
5835
5936def make_client_facades_from_generated_code (project_root : Path ) -> ClientFacades :
6037 """Return a client_facades dictionary from generated code under project_root.
6138 """
62- ignore_names = dir (_definitions )
63- excluded_facades = connection .excluded_facades
64- facades : Dict [str , List [int ]] = {}
39+ files_by_version : List [Tuple [int , Path ]] = []
40+ # [(facade_version, Path), ...]
6541 for file in (project_root / 'juju' / 'client' ).glob ('_client[0-9]*.py' ):
42+ files_by_version .append ((_version_from_filename (file ), file ))
43+ files_by_version .sort ()
44+
45+ # _clientN.py files import * from _definitions
46+ # so we will ignore any names from there
47+ ignore = dir (_definitions )
48+
49+ facades_by_version : Dict [int , Set [str ]] = {}
50+ # {facade_version: {facade_name, ...}, ...}
51+ for version , file in files_by_version :
6652 module = importlib .import_module (f'juju.client.{ file .stem } ' )
67- for cls_name in dir (module ):
68- if cls_name .startswith ('_' ) or cls_name in ignore_names :
69- continue
70- cls = getattr (module , cls_name )
71- if cls .version in excluded_facades .get (cls .name , []):
53+ facades = {
54+ name .removesuffix ("Facade" )
55+ for name in dir (module )
56+ if not (name .startswith ('_' ) or name in ignore )
57+ }
58+ facades_by_version [version ] = facades
59+
60+ # client_facades in connection.py is sorted
61+ # so we sort facade names before constructing it
62+ first , * rest = facades_by_version .values ()
63+ sorted_facade_names : list [str ] = sorted (first .union (* rest ))
64+
65+ excluded_facades = connection .excluded_facades
66+ client_facades : ClientFacades = {}
67+ # {facade_name: {'versions': [1, 2, 3, ...]}, ...}
68+ for name in sorted_facade_names :
69+ versions : List [int ] = []
70+ for version , facades in facades_by_version .items ():
71+ if version in excluded_facades .get (name , []):
7272 continue
73- facades .setdefault (cls .name , []).append (cls .version )
74- return {name : {'versions' : sorted (facades [name ])} for name in sorted (facades )}
73+ if name in facades :
74+ versions .append (version )
75+ client_facades [name ] = {'versions' : versions }
76+ return client_facades
77+
78+
79+ def _version_from_filename (path : Path ) -> int :
80+ match = re .search ('_client([0-9]+).py' , path .name )
81+ assert match
82+ return int (match .group (1 ))
0 commit comments