Coverage for src/lexigram/admin/resources/namespace.py: 0%

17 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:18 +0800

1"""Runtime namespace helper for contributor-supplied Resource classes.""" 

2 

3from __future__ import annotations 

4 

5import re 

6 

7_CACHE: dict[tuple[type, str], type] = {} 

8 

9 

10def _sanitize_package_segment(namespaced_name: str) -> str: 

11 """Return *namespaced_name* with non-slug characters replaced by underscores. 

12 

13 Contributor package sources are distribution names, where hyphens are 

14 legal ("lexigram-template"); resource slugs only allow ``[a-z0-9_]``. 

15 The package portion is sanitized so the namespaced name passes 

16 :func:`lexigram.admin.resources.base._validate_resource_name`. 

17 

18 Args: 

19 namespaced_name: Dotted provider-package name. 

20 

21 Returns: 

22 The sanitized dotted name. 

23 """ 

24 package, _, slug = namespaced_name.partition(".") 

25 safe_package = re.sub(r"[^a-z0-9_]", "_", package.lower()) 

26 return f"{safe_package}.{slug}" if slug else safe_package 

27 

28 

29def apply_namespace(resource_cls: type, namespaced_name: str) -> type: 

30 """Return a subclass of *resource_cls* with its name set to *namespaced_name*. 

31 

32 The returned class inherits all attributes from the original and adds a 

33 ``route_prefix`` derived from the dotted name: 

34 ``"fake_pkg.users"`` → ``route_prefix = "/fake_pkg/users"``. 

35 

36 Idempotent: calling twice with the same arguments returns the same class. 

37 """ 

38 safe_name = _sanitize_package_segment(namespaced_name) 

39 key = (resource_cls, safe_name) 

40 if key in _CACHE: 

41 return _CACHE[key] 

42 

43 package, _, slug = safe_name.partition(".") 

44 route_prefix = f"/{package}/{slug}" 

45 

46 wrapped = type( 

47 f"Namespaced_{resource_cls.__name__}", 

48 (resource_cls,), 

49 { 

50 "name": safe_name, 

51 "route_prefix": route_prefix, 

52 }, 

53 ) 

54 _CACHE[key] = wrapped 

55 return wrapped