crossplane-function-pythonic 0.6.2__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crossplane/pythonic/__about__.py +6 -2
- crossplane/pythonic/__init__.py +21 -13
- crossplane/pythonic/__main__.py +1 -0
- crossplane/pythonic/auto_ready.py +118 -61
- crossplane/pythonic/command.py +69 -58
- crossplane/pythonic/composite.py +656 -428
- crossplane/pythonic/function.py +233 -130
- crossplane/pythonic/grpc.py +89 -65
- crossplane/pythonic/main.py +18 -5
- crossplane/pythonic/packages.py +82 -51
- crossplane/pythonic/proto/v1/run_function_pb2.py +3 -3
- crossplane/pythonic/proto/v1/run_function_pb2.pyi +1121 -255
- crossplane/pythonic/proto/v1/run_function_pb2_grpc.py +4 -4
- crossplane/pythonic/proto/v1/run_function_pb2_grpc.pyi +64 -0
- crossplane/pythonic/protobuf.py +589 -372
- crossplane/pythonic/render.py +568 -235
- crossplane/pythonic/version.py +3 -4
- {crossplane_function_pythonic-0.6.2.dist-info → crossplane_function_pythonic-0.7.0.dist-info}/METADATA +23 -26
- crossplane_function_pythonic-0.7.0.dist-info/RECORD +22 -0
- {crossplane_function_pythonic-0.6.2.dist-info → crossplane_function_pythonic-0.7.0.dist-info}/WHEEL +1 -1
- {crossplane_function_pythonic-0.6.2.dist-info → crossplane_function_pythonic-0.7.0.dist-info}/entry_points.txt +1 -0
- crossplane_function_pythonic-0.6.2.dist-info/RECORD +0 -22
- crossplane_function_pythonic-0.6.2.dist-info/licenses/LICENSE +0 -201
crossplane/pythonic/grpc.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import argparse
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
@@ -16,88 +16,98 @@ from . import (
|
|
|
16
16
|
)
|
|
17
17
|
from .proto.v1 import run_function_pb2_grpc as grpcv1
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
logger = logging.getLogger(__name__)
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class Command(command.Command):
|
|
24
|
-
name =
|
|
25
|
-
help =
|
|
23
|
+
name = "grpc"
|
|
24
|
+
help = "Run function-pythonic gRPC server"
|
|
26
25
|
|
|
27
26
|
@classmethod
|
|
28
|
-
def add_parser_arguments(cls, parser):
|
|
27
|
+
def add_parser_arguments(cls, parser: argparse.ArgumentParser) -> None:
|
|
29
28
|
cls.add_function_arguments(parser)
|
|
30
29
|
parser.add_argument(
|
|
31
|
-
|
|
32
|
-
default=
|
|
33
|
-
help=
|
|
30
|
+
"--address",
|
|
31
|
+
default="0.0.0.0:9443",
|
|
32
|
+
help="Address to listen on for gRPC connections, default: 0.0.0.0:9443",
|
|
34
33
|
)
|
|
35
34
|
parser.add_argument(
|
|
36
|
-
|
|
37
|
-
default=os.getenv(
|
|
38
|
-
metavar=
|
|
39
|
-
help=
|
|
35
|
+
"--tls-certs-dir",
|
|
36
|
+
default=os.getenv("TLS_SERVER_CERTS_DIR"),
|
|
37
|
+
metavar="DIRECTORY",
|
|
38
|
+
help="Serve using TLS certificates.",
|
|
40
39
|
)
|
|
41
40
|
parser.add_argument(
|
|
42
|
-
|
|
43
|
-
action=
|
|
44
|
-
help=
|
|
41
|
+
"--insecure",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="Run without mTLS credentials, --tls-certs-dir will be ignored.",
|
|
45
44
|
)
|
|
46
45
|
parser.add_argument(
|
|
47
|
-
|
|
48
|
-
action=
|
|
49
|
-
dest=
|
|
50
|
-
help=
|
|
46
|
+
"--packages",
|
|
47
|
+
action="store_true",
|
|
48
|
+
dest="packages_configmaps",
|
|
49
|
+
help="Discover python packages from function-pythonic ConfigMaps, deprecated use --packages-configmaps",
|
|
51
50
|
)
|
|
52
51
|
parser.add_argument(
|
|
53
|
-
|
|
54
|
-
action=
|
|
55
|
-
help=
|
|
52
|
+
"--packages-configmaps",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Discover python packages from function-pythonic ConfigMaps.",
|
|
56
55
|
)
|
|
57
56
|
parser.add_argument(
|
|
58
|
-
|
|
59
|
-
action=
|
|
60
|
-
help=
|
|
57
|
+
"--packages-secrets",
|
|
58
|
+
action="store_true",
|
|
59
|
+
help="Also Discover python packages from function-pythonic Secrets.",
|
|
61
60
|
)
|
|
62
61
|
parser.add_argument(
|
|
63
|
-
|
|
64
|
-
action=
|
|
62
|
+
"--packages-namespace",
|
|
63
|
+
action="append",
|
|
65
64
|
default=[],
|
|
66
|
-
metavar=
|
|
67
|
-
help=
|
|
65
|
+
metavar="NAMESPACE",
|
|
66
|
+
help="Namespaces to discover function-pythonic ConfigMaps and Secrets in, default is cluster wide.",
|
|
68
67
|
)
|
|
69
68
|
parser.add_argument(
|
|
70
|
-
|
|
71
|
-
action=
|
|
72
|
-
help=
|
|
69
|
+
"--packages-environmentconfigs",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="Also Discover python packages from function-pythonic EnvironmentConfigs.",
|
|
73
72
|
)
|
|
74
73
|
parser.add_argument(
|
|
75
|
-
|
|
76
|
-
action=
|
|
77
|
-
help=
|
|
74
|
+
"--packages-compositions",
|
|
75
|
+
action="store_true",
|
|
76
|
+
help="Also Discover python packages from function-pythonic Compositions.",
|
|
78
77
|
)
|
|
79
78
|
parser.add_argument(
|
|
80
|
-
|
|
81
|
-
default=
|
|
82
|
-
metavar=
|
|
83
|
-
help='Directory to store discovered function-pythonic ConfigMaps to, defaults "<cwd>/pythonic-packages"'
|
|
79
|
+
"--packages-dir",
|
|
80
|
+
default="./pythonic-packages",
|
|
81
|
+
metavar="DIRECTORY",
|
|
82
|
+
help='Directory to store discovered function-pythonic ConfigMaps to, defaults "<cwd>/pythonic-packages"',
|
|
84
83
|
)
|
|
85
84
|
parser.add_argument(
|
|
86
|
-
|
|
87
|
-
metavar=
|
|
88
|
-
help=
|
|
85
|
+
"--pip-install",
|
|
86
|
+
metavar="INSTALL",
|
|
87
|
+
help="Pip install command to install additional Python packages.",
|
|
89
88
|
)
|
|
90
89
|
|
|
91
|
-
def initialize(self):
|
|
90
|
+
def initialize(self) -> None:
|
|
92
91
|
if not self.args.tls_certs_dir and not self.args.insecure:
|
|
93
|
-
print(
|
|
92
|
+
print(
|
|
93
|
+
"Either --tls-certs-dir or --insecure must be specified",
|
|
94
|
+
file=sys.stderr,
|
|
95
|
+
)
|
|
94
96
|
sys.exit(1)
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
+
if (
|
|
98
|
+
self.args.packages_environmentconfigs or self.args.packages_compositions
|
|
99
|
+
) and self.args.packages_namespace:
|
|
100
|
+
print(
|
|
101
|
+
"--packages-namespace cannot be used with --packages-environment-configs or --packages-compositions",
|
|
102
|
+
file=sys.stderr,
|
|
103
|
+
)
|
|
97
104
|
sys.exit(1)
|
|
98
105
|
if self.args.pip_install:
|
|
99
106
|
import pip._internal.cli.main
|
|
100
|
-
|
|
107
|
+
|
|
108
|
+
pip._internal.cli.main.main(
|
|
109
|
+
["install", "--user", *shlex.split(self.args.pip_install)]
|
|
110
|
+
)
|
|
101
111
|
|
|
102
112
|
self.initialize_function()
|
|
103
113
|
logger.info(f"Version: {__about__.__version__}")
|
|
@@ -105,9 +115,11 @@ class Command(command.Command):
|
|
|
105
115
|
# enables read only volumes or mismatched uid volumes
|
|
106
116
|
sys.dont_write_bytecode = True
|
|
107
117
|
|
|
108
|
-
async def run(self):
|
|
118
|
+
async def run(self) -> None:
|
|
109
119
|
grpc.aio.init_grpc_aio()
|
|
110
|
-
grpc_runner = function.FunctionRunner(
|
|
120
|
+
grpc_runner = function.FunctionRunner(
|
|
121
|
+
self.args.render_unknowns, self.args.crossplane_v1
|
|
122
|
+
)
|
|
111
123
|
grpc_server = grpc.aio.server()
|
|
112
124
|
grpcv1.add_FunctionRunnerServiceServicer_to_server(grpc_runner, grpc_server)
|
|
113
125
|
if self.args.insecure:
|
|
@@ -117,33 +129,45 @@ class Command(command.Command):
|
|
|
117
129
|
grpc_server.add_secure_port(
|
|
118
130
|
self.args.address,
|
|
119
131
|
grpc.ssl_server_credentials(
|
|
120
|
-
private_key_certificate_chain_pairs=[
|
|
121
|
-
(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
private_key_certificate_chain_pairs=[
|
|
133
|
+
(
|
|
134
|
+
(certs / "tls.key").read_bytes(),
|
|
135
|
+
(certs / "tls.crt").read_bytes(),
|
|
136
|
+
)
|
|
137
|
+
],
|
|
138
|
+
root_certificates=(certs / "ca.crt").read_bytes(),
|
|
125
139
|
require_client_auth=True,
|
|
126
140
|
),
|
|
127
141
|
)
|
|
128
142
|
await grpc_server.start()
|
|
129
143
|
|
|
130
|
-
if
|
|
144
|
+
if (
|
|
145
|
+
self.args.packages_configmaps
|
|
146
|
+
or self.args.packages_secrets
|
|
147
|
+
or self.args.packages_environmentconfigs
|
|
148
|
+
or self.args.packages_compositions
|
|
149
|
+
):
|
|
131
150
|
from . import packages
|
|
151
|
+
|
|
132
152
|
async with asyncio.TaskGroup() as tasks:
|
|
133
153
|
tasks.create_task(grpc_server.wait_for_termination())
|
|
134
|
-
tasks.create_task(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
tasks.create_task(
|
|
155
|
+
packages.operator(
|
|
156
|
+
grpc_server,
|
|
157
|
+
grpc_runner,
|
|
158
|
+
self.args.packages_configmaps,
|
|
159
|
+
self.args.packages_secrets,
|
|
160
|
+
self.args.packages_namespace,
|
|
161
|
+
self.args.packages_environmentconfigs,
|
|
162
|
+
self.args.packages_compositions,
|
|
163
|
+
self.args.packages_dir,
|
|
164
|
+
)
|
|
165
|
+
)
|
|
144
166
|
else:
|
|
167
|
+
|
|
145
168
|
def stop():
|
|
146
169
|
asyncio.ensure_future(grpc_server.stop(5))
|
|
170
|
+
|
|
147
171
|
loop = asyncio.get_event_loop()
|
|
148
172
|
loop.add_signal_handler(signal.SIGINT, stop)
|
|
149
173
|
loop.add_signal_handler(signal.SIGTERM, stop)
|
crossplane/pythonic/main.py
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
# kr8s emits this at import time, so filter before importing it.
|
|
9
|
+
warnings.filterwarnings(
|
|
10
|
+
"ignore",
|
|
11
|
+
message="'asyncio.iscoroutinefunction' is deprecated",
|
|
12
|
+
category=DeprecationWarning,
|
|
13
|
+
module="kr8s._vendored.asyncache",
|
|
14
|
+
)
|
|
6
15
|
|
|
7
16
|
from . import (
|
|
8
17
|
grpc,
|
|
@@ -12,17 +21,21 @@ from . import (
|
|
|
12
21
|
|
|
13
22
|
|
|
14
23
|
def main():
|
|
15
|
-
parser = argparse.ArgumentParser(
|
|
16
|
-
|
|
24
|
+
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
|
25
|
+
"Crossplane Function Pythonic"
|
|
26
|
+
)
|
|
27
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser] = (
|
|
28
|
+
parser.add_subparsers(title="Command", metavar="")
|
|
29
|
+
)
|
|
17
30
|
grpc.Command.create(subparsers)
|
|
18
31
|
render.Command.create(subparsers)
|
|
19
32
|
version.Command.create(subparsers)
|
|
20
|
-
args = parser.parse_args()
|
|
21
|
-
if not hasattr(args,
|
|
33
|
+
args: argparse.Namespace = parser.parse_args()
|
|
34
|
+
if not hasattr(args, "command"):
|
|
22
35
|
parser.print_help()
|
|
23
36
|
sys.exit(1)
|
|
24
37
|
asyncio.run(args.command(args).run())
|
|
25
38
|
|
|
26
39
|
|
|
27
|
-
if __name__ ==
|
|
40
|
+
if __name__ == "__main__":
|
|
28
41
|
main()
|
crossplane/pythonic/packages.py
CHANGED
|
@@ -1,42 +1,54 @@
|
|
|
1
|
-
|
|
2
1
|
import base64
|
|
3
2
|
import logging
|
|
4
3
|
import pathlib
|
|
5
4
|
import sys
|
|
5
|
+
import typing
|
|
6
6
|
|
|
7
|
+
import grpc
|
|
7
8
|
import kopf
|
|
8
9
|
|
|
10
|
+
from . import function
|
|
9
11
|
|
|
10
12
|
GRPC_SERVER = None
|
|
11
13
|
GRPC_RUNNER = None
|
|
12
14
|
PACKAGES_DIR = None
|
|
13
|
-
PACKAGE_LABEL =
|
|
15
|
+
PACKAGE_LABEL = "function-pythonic.package"
|
|
14
16
|
PACKAGE_LABELS = {PACKAGE_LABEL: kopf.PRESENT}
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def operator(
|
|
18
|
-
|
|
19
|
+
def operator(
|
|
20
|
+
grpc_server: grpc.aio.Server,
|
|
21
|
+
grpc_runner: function.FunctionRunner,
|
|
22
|
+
packages_configmaps: bool,
|
|
23
|
+
packages_secrets: bool,
|
|
24
|
+
packages_namespaces: list[str],
|
|
25
|
+
packages_environmentconfigs: bool,
|
|
26
|
+
packages_compositions: bool,
|
|
27
|
+
packages_dir: str,
|
|
28
|
+
) -> typing.Coroutine:
|
|
29
|
+
logging.getLogger("kopf.objects").setLevel(logging.INFO)
|
|
19
30
|
global GRPC_SERVER, GRPC_RUNNER, PACKAGES_DIR
|
|
20
31
|
GRPC_SERVER = grpc_server
|
|
21
32
|
GRPC_RUNNER = grpc_runner
|
|
22
33
|
PACKAGES_DIR = pathlib.Path(packages_dir).expanduser().resolve()
|
|
23
34
|
sys.path.insert(0, str(PACKAGES_DIR))
|
|
24
35
|
if packages_configmaps:
|
|
25
|
-
on_resource(
|
|
36
|
+
on_resource("", "v1", "configmaps")
|
|
26
37
|
if packages_secrets:
|
|
27
|
-
on_resource(
|
|
38
|
+
on_resource("", "v1", "secrets")
|
|
28
39
|
if not packages_namespaces:
|
|
29
40
|
if packages_environmentconfigs:
|
|
30
|
-
on_resource(
|
|
41
|
+
on_resource("apiextensions.crossplane.io", "v1beta1", "environmentconfigs")
|
|
31
42
|
if packages_compositions:
|
|
32
|
-
on_resource(
|
|
43
|
+
on_resource("apiextensions.crossplane.io", "v1", "compositions")
|
|
33
44
|
return kopf.operator(
|
|
34
45
|
standalone=True,
|
|
35
46
|
clusterwide=not packages_namespaces,
|
|
36
47
|
namespaces=packages_namespaces,
|
|
37
48
|
)
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
|
|
51
|
+
def on_resource(group: str, version: str, plural: str):
|
|
40
52
|
kopf.on.create(group, version, plural, labels=PACKAGE_LABELS)(create)
|
|
41
53
|
kopf.on.resume(group, version, plural, labels=PACKAGE_LABELS)(create)
|
|
42
54
|
kopf.on.update(group, version, plural, labels=PACKAGE_LABELS)(update)
|
|
@@ -44,52 +56,66 @@ def on_resource(group, version, plural):
|
|
|
44
56
|
|
|
45
57
|
|
|
46
58
|
@kopf.on.startup()
|
|
47
|
-
async def startup(settings, **_):
|
|
59
|
+
async def startup(settings: kopf.OperatorSettings, **_: typing.Any) -> None:
|
|
48
60
|
settings.scanning.disabled = True
|
|
49
61
|
|
|
50
62
|
|
|
51
63
|
@kopf.on.cleanup()
|
|
52
|
-
async def cleanup(**_):
|
|
53
|
-
|
|
64
|
+
async def cleanup(**_: typing.Any) -> None:
|
|
65
|
+
if GRPC_SERVER:
|
|
66
|
+
await GRPC_SERVER.stop(5)
|
|
54
67
|
|
|
55
68
|
|
|
56
|
-
async def create(resource, labels, body, logger, **_):
|
|
57
|
-
resource_create(resource, labels,
|
|
69
|
+
async def create(resource, labels, body, logger, **_: typing.Any):
|
|
70
|
+
resource_create(resource, labels, "Created", body, logger)
|
|
58
71
|
|
|
59
72
|
|
|
60
|
-
async def update(resource, labels, body, old, logger, **_):
|
|
61
|
-
resource_delete(resource, labels,
|
|
62
|
-
resource_create(resource, labels,
|
|
73
|
+
async def update(resource, labels, body, old, logger, **_: typing.Any):
|
|
74
|
+
resource_delete(resource, labels, "Removed", old, logger)
|
|
75
|
+
resource_create(resource, labels, "Added", body, logger)
|
|
63
76
|
|
|
64
77
|
|
|
65
|
-
async def delete(resource, labels, body, logger, **_):
|
|
66
|
-
resource_delete(resource, labels,
|
|
78
|
+
async def delete(resource, labels, body, logger, **_: typing.Any):
|
|
79
|
+
resource_delete(resource, labels, "Deleted", body, logger)
|
|
67
80
|
|
|
68
81
|
|
|
69
82
|
def resource_create(resource, labels, action, body, logger):
|
|
70
83
|
package_dir = resource_package_dir(resource, labels, logger)
|
|
71
84
|
if not package_dir:
|
|
72
85
|
return
|
|
73
|
-
if resource.plural in (
|
|
74
|
-
package_create(resource, action, package_dir, body.get(
|
|
75
|
-
elif resource.plural ==
|
|
76
|
-
for step in body.get(
|
|
77
|
-
input = step.get(
|
|
78
|
-
if
|
|
79
|
-
|
|
86
|
+
if resource.plural in ("configmaps", "secrets", "environmentconfigs"):
|
|
87
|
+
package_create(resource, action, package_dir, body.get("data", {}), logger)
|
|
88
|
+
elif resource.plural == "compositions":
|
|
89
|
+
for step in body.get("spec", {}).get("pipeline", []):
|
|
90
|
+
input = step.get("input")
|
|
91
|
+
if (
|
|
92
|
+
input
|
|
93
|
+
and input.get("apiVersion") == "pythonic.fn.crossplane.io/v1alpha1"
|
|
94
|
+
):
|
|
95
|
+
package_create(
|
|
96
|
+
resource, action, package_dir, input.get("packages", {}), logger
|
|
97
|
+
)
|
|
80
98
|
|
|
81
99
|
|
|
82
100
|
def resource_delete(resource, labels, action, body, logger):
|
|
83
101
|
package_dir = resource_package_dir(resource, labels, logger)
|
|
84
102
|
if not package_dir:
|
|
85
103
|
return
|
|
86
|
-
if resource.plural in (
|
|
87
|
-
package_delete(action, package_dir, body.get(
|
|
88
|
-
elif resource.plural ==
|
|
89
|
-
for step in body.get(
|
|
90
|
-
input = step.get(
|
|
91
|
-
if
|
|
92
|
-
|
|
104
|
+
if resource.plural in ("configmaps", "secrets", "environmentconfigs"):
|
|
105
|
+
package_delete(action, package_dir, body.get("data", {}), logger)
|
|
106
|
+
elif resource.plural == "compositions":
|
|
107
|
+
for step in body.get("spec", {}).get("pipeline", []):
|
|
108
|
+
input = step.get("input")
|
|
109
|
+
if (
|
|
110
|
+
input
|
|
111
|
+
and input.get("apiVersion") == "pythonic.fn.crossplane.io/v1alpha1"
|
|
112
|
+
):
|
|
113
|
+
package_delete(
|
|
114
|
+
action,
|
|
115
|
+
package_dir,
|
|
116
|
+
step.get("input", {}).get("packages", {}),
|
|
117
|
+
logger,
|
|
118
|
+
)
|
|
93
119
|
|
|
94
120
|
|
|
95
121
|
def resource_package_dir(resource, labels, logger):
|
|
@@ -99,10 +125,10 @@ def resource_package_dir(resource, labels, logger):
|
|
|
99
125
|
logger.error(f"{PACKAGE_LABEL} label is missing")
|
|
100
126
|
return None
|
|
101
127
|
package_dir = PACKAGES_DIR
|
|
102
|
-
if resource.plural in (
|
|
103
|
-
for segment in package.split(
|
|
128
|
+
if resource.plural in ("configmaps", "secrets") and package:
|
|
129
|
+
for segment in package.split("."):
|
|
104
130
|
if not segment.isidentifier():
|
|
105
|
-
logger.error(
|
|
131
|
+
logger.error("Package has invalid package name: %s", package)
|
|
106
132
|
return None
|
|
107
133
|
package_dir = package_dir / segment
|
|
108
134
|
return package_dir
|
|
@@ -114,13 +140,14 @@ def package_create(resource, action, package_dir, package, logger):
|
|
|
114
140
|
package_name = package_dir / name
|
|
115
141
|
if isinstance(value, str):
|
|
116
142
|
package_name.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
-
if resource.plural ==
|
|
118
|
-
package_name.write_bytes(base64.b64decode(value.encode(
|
|
143
|
+
if resource.plural == "secrets":
|
|
144
|
+
package_name.write_bytes(base64.b64decode(value.encode("utf-8")))
|
|
119
145
|
else:
|
|
120
146
|
package_name.write_text(value)
|
|
121
147
|
module, name = package_file_name(package_name)
|
|
122
148
|
if module:
|
|
123
|
-
GRPC_RUNNER
|
|
149
|
+
if GRPC_RUNNER:
|
|
150
|
+
GRPC_RUNNER.invalidate_module(name)
|
|
124
151
|
logger.info(f"{action} module: {name}")
|
|
125
152
|
else:
|
|
126
153
|
logger.info(f"{action} file: {name}")
|
|
@@ -136,20 +163,22 @@ def package_delete(action, package_dir, package, logger):
|
|
|
136
163
|
package_name.unlink(missing_ok=True)
|
|
137
164
|
module, name = package_file_name(package_name)
|
|
138
165
|
if module:
|
|
139
|
-
GRPC_RUNNER
|
|
166
|
+
if GRPC_RUNNER:
|
|
167
|
+
GRPC_RUNNER.invalidate_module(name)
|
|
140
168
|
logger.info(f"{action} module: {name}")
|
|
141
169
|
else:
|
|
142
170
|
logger.info(f"{action} file: {name}")
|
|
143
171
|
parent = package_name.parent
|
|
144
172
|
while (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
173
|
+
parent.is_relative_to(PACKAGES_DIR)
|
|
174
|
+
and parent.is_dir()
|
|
175
|
+
and not list(parent.iterdir())
|
|
148
176
|
):
|
|
149
177
|
parent.rmdir()
|
|
150
|
-
module = str(parent.relative_to(PACKAGES_DIR)).replace(
|
|
151
|
-
if module !=
|
|
152
|
-
GRPC_RUNNER
|
|
178
|
+
module = str(parent.relative_to(PACKAGES_DIR)).replace("/", ".")
|
|
179
|
+
if module != ".":
|
|
180
|
+
if GRPC_RUNNER:
|
|
181
|
+
GRPC_RUNNER.invalidate_module(module)
|
|
153
182
|
logger.info(f"{action} package: {module}")
|
|
154
183
|
parent = parent.parent
|
|
155
184
|
elif isinstance(value, dict):
|
|
@@ -158,13 +187,13 @@ def package_delete(action, package_dir, package, logger):
|
|
|
158
187
|
|
|
159
188
|
def validate_entry(name, value, logger):
|
|
160
189
|
if isinstance(value, str):
|
|
161
|
-
if name.endswith(
|
|
190
|
+
if name.endswith(".py"):
|
|
162
191
|
name = name[:-3]
|
|
163
192
|
if not name.isidentifier():
|
|
164
193
|
logger.error(f"Python module name is not an identifier: {name}")
|
|
165
194
|
return False
|
|
166
195
|
else:
|
|
167
|
-
if
|
|
196
|
+
if "/" in name:
|
|
168
197
|
logger.error(f"Python file name is not valid: {name}")
|
|
169
198
|
return False
|
|
170
199
|
elif isinstance(value, dict):
|
|
@@ -172,13 +201,15 @@ def validate_entry(name, value, logger):
|
|
|
172
201
|
logger.error(f"Python package name is not an identifier: {name}")
|
|
173
202
|
return False
|
|
174
203
|
else:
|
|
175
|
-
logger.error(
|
|
204
|
+
logger.error(
|
|
205
|
+
f'Python package "{name}" value is not a valid type: {value.__class__}'
|
|
206
|
+
)
|
|
176
207
|
return False
|
|
177
208
|
return True
|
|
178
209
|
|
|
179
210
|
|
|
180
211
|
def package_file_name(package_name):
|
|
181
212
|
name = str(package_name.relative_to(PACKAGES_DIR))
|
|
182
|
-
if name.endswith(
|
|
183
|
-
return True, name[:-3].replace(
|
|
213
|
+
if name.endswith(".py"):
|
|
214
|
+
return True, name[:-3].replace("/", ".")
|
|
184
215
|
return False, name
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
4
|
# source: crossplane/pythonic/proto/v1/run_function.proto
|
|
5
|
-
# Protobuf Python Version:
|
|
5
|
+
# Protobuf Python Version: 7.35.1
|
|
6
6
|
"""Generated protocol buffer code."""
|
|
7
7
|
from google.protobuf import descriptor as _descriptor
|
|
8
8
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
@@ -11,8 +11,8 @@ from google.protobuf import symbol_database as _symbol_database
|
|
|
11
11
|
from google.protobuf.internal import builder as _builder
|
|
12
12
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
13
|
_runtime_version.Domain.PUBLIC,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
7,
|
|
15
|
+
35,
|
|
16
16
|
1,
|
|
17
17
|
'',
|
|
18
18
|
'crossplane/pythonic/proto/v1/run_function.proto'
|