5
5
import base64
6
6
import json
7
7
import os
8
+ import pkgutil
8
9
import sys
9
10
import types
10
11
import zlib
13
14
from importlib .metadata import Distribution
14
15
from importlib .metadata import DistributionFinder
15
16
from typing import IO
16
- from typing import TYPE_CHECKING
17
17
from typing import Any
18
18
from typing import Iterable
19
+ from typing import NamedTuple
20
+ from typing import NewType
21
+ from typing import Protocol
19
22
from typing import Sequence
23
+ from typing import cast
24
+ from typing import runtime_checkable
20
25
21
- if TYPE_CHECKING :
22
- pass
26
+ ModuleName = NewType ("ModuleName" , str )
23
27
24
28
25
29
class DictDistribution (Distribution ):
@@ -35,56 +39,58 @@ def locate_file(self, path: str | os.PathLike[str]) -> os.PathLike[str]:
35
39
raise FileNotFoundError (path )
36
40
37
41
42
+ class ModuleInfo (NamedTuple ):
43
+ name : ModuleName
44
+ is_pkg : bool
45
+ source : str
46
+
47
+
38
48
class DictImporter (DistributionFinder , Loader ):
39
49
"""a limited loader/importer for distributins send via json-lines"""
40
50
41
- def __init__ (self , sources : dict [str , str ], distribution : DictDistribution ):
51
+ def __init__ (
52
+ self , sources : dict [ModuleName , ModuleInfo ], distribution : DictDistribution
53
+ ):
42
54
self .sources = sources
43
55
self .distribution = distribution
44
56
45
57
def find_distributions (
46
- self , context : DistributionFinder .Context = DistributionFinder . Context ()
58
+ self , context : DistributionFinder .Context | None = None
47
59
) -> Iterable [Distribution ]:
60
+ # TODO: filter
48
61
return [self .distribution ]
49
62
50
63
def find_module (
51
64
self , fullname : str , path : Sequence [str | bytes ] | None = None
52
65
) -> Loader | None :
53
- if fullname in self .sources :
54
- return self
55
- if fullname + ".__init__" in self .sources :
66
+ if ModuleName (fullname ) in self .sources :
56
67
return self
57
68
return None
58
69
59
- def load_module (self , fullname ) :
70
+ def load_module (self , fullname : str ) -> types . ModuleType :
60
71
# print "load_module:", fullname
61
- from types import ModuleType
62
72
63
- try :
64
- s = self .sources [fullname ]
65
- is_pkg = False
66
- except KeyError :
67
- s = self .sources [fullname + ".__init__" ]
68
- is_pkg = True
73
+ info = self .sources [ModuleName (fullname )]
69
74
70
- co = compile (s , fullname , "exec" )
71
- module = sys .modules .setdefault (fullname , ModuleType (fullname ))
75
+ co = compile (info . source , fullname , "exec" )
76
+ module = sys .modules .setdefault (fullname , types . ModuleType (fullname ))
72
77
module .__loader__ = self
73
- if is_pkg :
78
+ if info . is_pkg :
74
79
module .__path__ = [fullname ]
75
80
76
81
exec (co , module .__dict__ )
77
82
return sys .modules [fullname ]
78
83
79
84
def get_source (self , name : str ) -> str | None :
80
- res = self .sources .get (name )
85
+ res = self .sources .get (ModuleName ( name ) )
81
86
if res is None :
82
- res = self .sources .get (name + ".__init__" )
83
- return res
87
+ return None
88
+ else :
89
+ return res .source
84
90
85
91
86
92
def bootstrap (
87
- modules : dict [str , str ],
93
+ modules : dict [ModuleName , ModuleInfo ],
88
94
distribution : dict [str , str ],
89
95
entry : str ,
90
96
args : dict [str , Any ],
@@ -100,7 +106,7 @@ def bootstrap(
100
106
entry_func (** args )
101
107
102
108
103
- def bootstrap_stdin (stream : IO ) -> None :
109
+ def bootstrap_stdin (stream : IO [ bytes ] | IO [ str ] ) -> None :
104
110
bootstrap_args = decode_b85_zip_json (stream .readline ())
105
111
bootstrap (** bootstrap_args )
106
112
@@ -111,9 +117,26 @@ def decode_b85_zip_json(encoded: bytes | str):
111
117
return json .loads (unpacked )
112
118
113
119
114
- def naive_pack_module (module : types .ModuleType , dist : Distribution ):
120
+ @runtime_checkable
121
+ class SourceProvidingLoader (Protocol ):
122
+ def get_source (self , name : str ) -> str :
123
+ ...
124
+
125
+
126
+ def naive_pack_module (module : types .ModuleType , dist : Distribution ) -> object :
115
127
assert module .__file__ is not None
116
128
assert module .__path__
129
+ data : dict [ModuleName , ModuleInfo ] = {}
130
+ for info in pkgutil .walk_packages (module .__path__ , f"{ module .__name__ } ." ):
131
+ spec = info .module_finder .find_spec (info .name , None )
132
+ assert spec is not None
133
+ loader = cast (SourceProvidingLoader , spec .loader )
134
+
135
+ source = loader .get_source (info .name )
136
+ data [ModuleName (info .name )] = ModuleInfo (
137
+ name = ModuleName (info .name ), is_pkg = info .ispkg , source = source
138
+ )
139
+ return data
117
140
118
141
119
142
if __name__ == "__main__" :
0 commit comments