1#     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2#
3#     Part of "Nuitka", an optimizing Python compiler that is compatible and
4#     integrates with CPython, but also works on its own.
5#
6#     Licensed under the Apache License, Version 2.0 (the "License");
7#     you may not use this file except in compliance with the License.
8#     You may obtain a copy of the License at
9#
10#        http://www.apache.org/licenses/LICENSE-2.0
11#
12#     Unless required by applicable law or agreed to in writing, software
13#     distributed under the License is distributed on an "AS IS" BASIS,
14#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#     See the License for the specific language governing permissions and
16#     limitations under the License.
17#
18"""
19Namespace packages of Python3.3 or higher
20
21"""
22
23import os
24
25from nuitka import Options
26from nuitka.nodes.AssignNodes import StatementAssignmentVariableName
27from nuitka.nodes.AttributeNodes import ExpressionAttributeLookup
28from nuitka.nodes.CallNodes import ExpressionCallNoKeywords
29from nuitka.nodes.ConstantRefNodes import makeConstantRefNode
30from nuitka.nodes.ContainerMakingNodes import (
31    makeExpressionMakeList,
32    makeExpressionMakeTuple,
33    makeExpressionMakeTupleOrConstant,
34)
35from nuitka.nodes.DictionaryNodes import StatementDictOperationSet
36from nuitka.nodes.FutureSpecs import FutureSpec
37from nuitka.nodes.ImportNodes import (
38    ExpressionImportModuleNameHard,
39    ExpressionImportName,
40    makeExpressionAbsoluteImportNode,
41)
42from nuitka.nodes.ModuleAttributeNodes import (
43    ExpressionModuleAttributeFileRef,
44    ExpressionNuitkaLoaderCreation,
45)
46from nuitka.nodes.ModuleNodes import CompiledPythonPackage
47from nuitka.nodes.SubscriptNodes import ExpressionSubscriptLookup
48from nuitka.nodes.VariableRefNodes import ExpressionVariableNameRef
49from nuitka.PythonVersions import python_version
50
51from .TreeHelpers import makeStatementsSequenceFromStatement
52from .VariableClosure import completeVariableClosures
53
54
55def _makeCall(module_name, import_name, attribute_name, source_ref, *args):
56    return ExpressionCallNoKeywords(
57        called=ExpressionAttributeLookup(
58            expression=ExpressionImportModuleNameHard(
59                module_name=module_name, import_name=import_name, source_ref=source_ref
60            ),
61            attribute_name=attribute_name,
62            source_ref=source_ref,
63        ),
64        args=makeExpressionMakeTupleOrConstant(
65            elements=args, user_provided=True, source_ref=source_ref
66        ),
67        source_ref=source_ref,
68    )
69
70
71def getNameSpacePathExpression(package, source_ref):
72    """Create the __path__ expression for a package."""
73
74    reference_mode = Options.getFileReferenceMode()
75
76    if reference_mode == "original":
77        return makeConstantRefNode(
78            constant=[package.getCompileTimeDirectory()],
79            source_ref=source_ref,
80        )
81    elif reference_mode == "frozen":
82        return makeConstantRefNode(
83            constant=[],
84            source_ref=source_ref,
85        )
86    else:
87        elements = [
88            ExpressionCallNoKeywords(
89                called=ExpressionAttributeLookup(
90                    expression=ExpressionImportModuleNameHard(
91                        module_name="os", import_name="path", source_ref=source_ref
92                    ),
93                    attribute_name="dirname",
94                    source_ref=source_ref,
95                ),
96                args=makeExpressionMakeTuple(
97                    elements=(
98                        ExpressionModuleAttributeFileRef(
99                            variable=package.getVariableForReference("__file__"),
100                            source_ref=source_ref,
101                        ),
102                    ),
103                    source_ref=source_ref,
104                ),
105                source_ref=source_ref,
106            )
107        ]
108
109        if package.canHaveExternalImports():
110            parts = package.getFullName().asString().split(".")
111
112            for count in range(len(parts)):
113                path_part = _makeCall(
114                    "os",
115                    "environ",
116                    "get",
117                    source_ref,
118                    makeConstantRefNode(
119                        constant="NUITKA_PACKAGE_%s" % "_".join(parts[: count + 1]),
120                        source_ref=source_ref,
121                    ),
122                    makeConstantRefNode(constant="/notexist", source_ref=source_ref),
123                )
124
125                if parts[count + 1 :]:
126                    path_part = _makeCall(
127                        "os",
128                        "path",
129                        "join",
130                        source_ref,
131                        path_part,
132                        makeConstantRefNode(
133                            constant=os.path.join(*parts[count + 1 :]),
134                            source_ref=source_ref,
135                        ),
136                    )
137
138                elements.append(path_part)
139
140        return makeExpressionMakeList(elements=elements, source_ref=source_ref)
141
142
143def createPathAssignment(package, source_ref):
144    return StatementAssignmentVariableName(
145        provider=package,
146        variable_name="__path__",
147        source=getNameSpacePathExpression(package=package, source_ref=source_ref),
148        source_ref=source_ref,
149    )
150
151
152def createPython3NamespacePath(package, source_ref):
153    return StatementAssignmentVariableName(
154        provider=package,
155        variable_name="__path__",
156        source=ExpressionCallNoKeywords(
157            called=ExpressionImportName(
158                module=makeExpressionAbsoluteImportNode(
159                    module_name="_frozen_importlib"
160                    if python_version < 0x350
161                    else "_frozen_importlib_external",
162                    source_ref=source_ref,
163                ),
164                import_name="_NamespacePath",
165                level=0,
166                source_ref=source_ref,
167            ),
168            args=makeExpressionMakeTupleOrConstant(
169                elements=(
170                    makeConstantRefNode(
171                        constant=package.getFullName().asString(),
172                        user_provided=True,
173                        source_ref=source_ref,
174                    ),
175                    getNameSpacePathExpression(package=package, source_ref=source_ref),
176                    makeConstantRefNode(constant=None, source_ref=source_ref),
177                ),
178                user_provided=True,
179                source_ref=source_ref,
180            ),
181            source_ref=source_ref,
182        ),
183        source_ref=source_ref,
184    )
185
186
187def createNamespacePackage(module_name, is_top, source_ref):
188    package = CompiledPythonPackage(
189        module_name=module_name,
190        is_top=is_top,
191        mode="compiled",
192        future_spec=FutureSpec(),
193        source_ref=source_ref,
194    )
195
196    if python_version >= 0x300:
197        statement = createPython3NamespacePath(package=package, source_ref=source_ref)
198    else:
199        statement = createPathAssignment(package, source_ref)
200
201    package.setChild("body", makeStatementsSequenceFromStatement(statement=statement))
202
203    completeVariableClosures(package)
204
205    return package
206
207
208def createImporterCacheAssignment(package, source_ref):
209    return StatementDictOperationSet(
210        dict_arg=ExpressionImportModuleNameHard(
211            module_name="sys", import_name="path_importer_cache", source_ref=source_ref
212        ),
213        key=ExpressionSubscriptLookup(
214            expression=ExpressionVariableNameRef(
215                provider=package, variable_name="__path__", source_ref=source_ref
216            ),
217            subscript=makeConstantRefNode(constant=0, source_ref=source_ref),
218            source_ref=source_ref,
219        ),
220        value=ExpressionNuitkaLoaderCreation(provider=package, source_ref=source_ref),
221        source_ref=source_ref,
222    )
223