cycls 0.0.2.94__tar.gz → 0.0.2.96__tar.gz
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.
- {cycls-0.0.2.94 → cycls-0.0.2.96}/PKG-INFO +1 -1
- cycls-0.0.2.96/cycls/__init__.py +6 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/app.py +4 -3
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/function.py +51 -6
- {cycls-0.0.2.94 → cycls-0.0.2.96}/pyproject.toml +1 -1
- cycls-0.0.2.94/cycls/__init__.py +0 -14
- {cycls-0.0.2.94 → cycls-0.0.2.96}/.gitignore +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/README.md +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/auth.py +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/cli.py +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/state.py +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/themes/default/assets/index-CTZe1T7l.js +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/themes/default/assets/index-oGkkm3Z8.css +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/themes/default/index.html +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/themes/dev/index.html +0 -0
- {cycls-0.0.2.94 → cycls-0.0.2.96}/cycls/web.py +0 -0
|
@@ -15,13 +15,14 @@ class App(Function):
|
|
|
15
15
|
|
|
16
16
|
def __init__(self, func, name, theme="default", pip=None, apt=None, copy=None, copy_public=None,
|
|
17
17
|
auth=False, org=None, header=None, intro=None, title=None, plan="free", analytics=False,
|
|
18
|
-
state=False):
|
|
18
|
+
state=False, memory="1Gi"):
|
|
19
19
|
if theme not in THEMES:
|
|
20
20
|
raise ValueError(f"Unknown theme: {theme}. Available: {THEMES}")
|
|
21
21
|
self.user_func = func
|
|
22
22
|
self.theme = theme
|
|
23
23
|
self.copy_public = copy_public or []
|
|
24
24
|
self.state = state
|
|
25
|
+
self.memory = memory
|
|
25
26
|
|
|
26
27
|
self.config = Config(
|
|
27
28
|
header=header,
|
|
@@ -79,12 +80,12 @@ class App(Function):
|
|
|
79
80
|
self._prepare_func(prod=True)
|
|
80
81
|
return super().deploy(port=port)
|
|
81
82
|
|
|
82
|
-
def _deploy(self, port=8080):
|
|
83
|
+
def _deploy(self, port=8080, memory=None):
|
|
83
84
|
"""Deploy to testing infrastructure."""
|
|
84
85
|
if self.api_key is None:
|
|
85
86
|
raise RuntimeError("Missing API key. Set cycls.api_key or CYCLS_API_KEY environment variable.")
|
|
86
87
|
self._prepare_func(prod=True)
|
|
87
|
-
return super()._deploy(port=port)
|
|
88
|
+
return super()._deploy(port=port, memory=memory or self.memory)
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
def app(name=None, **kwargs):
|
|
@@ -46,10 +46,14 @@ api_key = None
|
|
|
46
46
|
base_url = None
|
|
47
47
|
|
|
48
48
|
def _get_api_key():
|
|
49
|
-
|
|
49
|
+
import sys
|
|
50
|
+
cycls_pkg = sys.modules.get('cycls')
|
|
51
|
+
return api_key or (cycls_pkg and cycls_pkg.__dict__.get('api_key')) or os.getenv("CYCLS_API_KEY")
|
|
50
52
|
|
|
51
53
|
def _get_base_url():
|
|
52
|
-
|
|
54
|
+
import sys
|
|
55
|
+
cycls_pkg = sys.modules.get('cycls')
|
|
56
|
+
return base_url or (cycls_pkg and cycls_pkg.__dict__.get('base_url')) or os.getenv("CYCLS_BASE_URL")
|
|
53
57
|
|
|
54
58
|
def _hash_path(path_str: str) -> str:
|
|
55
59
|
h = hashlib.sha256()
|
|
@@ -91,8 +95,8 @@ class Function:
|
|
|
91
95
|
self.apt = sorted(apt or [])
|
|
92
96
|
self.run_commands = sorted(run_commands or [])
|
|
93
97
|
self.copy = {f: f for f in copy} if isinstance(copy, list) else (copy or {})
|
|
94
|
-
self.
|
|
95
|
-
self.
|
|
98
|
+
self._base_url = base_url
|
|
99
|
+
self._api_key = api_key
|
|
96
100
|
self.pip = sorted(set(pip or []) | {"cloudpickle"})
|
|
97
101
|
|
|
98
102
|
self.image_prefix = f"cycls/{self.name}"
|
|
@@ -100,6 +104,14 @@ class Function:
|
|
|
100
104
|
self._docker_client = None
|
|
101
105
|
self._container = None
|
|
102
106
|
|
|
107
|
+
@property
|
|
108
|
+
def api_key(self):
|
|
109
|
+
return self._api_key or _get_api_key()
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def base_url(self):
|
|
113
|
+
return self._base_url or _get_base_url() or "https://service-core-280879789566.me-central1.run.app"
|
|
114
|
+
|
|
103
115
|
@property
|
|
104
116
|
def docker_client(self):
|
|
105
117
|
if self._docker_client is None:
|
|
@@ -399,7 +411,31 @@ CMD ["python", "entrypoint.py"]
|
|
|
399
411
|
def _deploy(self, *args, **kwargs):
|
|
400
412
|
import requests
|
|
401
413
|
|
|
414
|
+
base_url = self.base_url
|
|
402
415
|
port = kwargs.pop('port', 8080)
|
|
416
|
+
memory = kwargs.pop('memory', '1Gi')
|
|
417
|
+
|
|
418
|
+
# Check name availability before uploading
|
|
419
|
+
print(f"Checking '{self.name}'...")
|
|
420
|
+
try:
|
|
421
|
+
check_resp = requests.get(
|
|
422
|
+
f"{base_url}/v1/deployment/check-name",
|
|
423
|
+
params={"name": self.name},
|
|
424
|
+
headers={"X-API-Key": self.api_key},
|
|
425
|
+
timeout=30,
|
|
426
|
+
)
|
|
427
|
+
if check_resp.status_code == 401:
|
|
428
|
+
print("Error: Invalid API key")
|
|
429
|
+
return None
|
|
430
|
+
check_resp.raise_for_status()
|
|
431
|
+
check_data = check_resp.json()
|
|
432
|
+
if not check_data.get("available"):
|
|
433
|
+
print(f"Error: {check_data.get('reason', 'Name unavailable')}")
|
|
434
|
+
return None
|
|
435
|
+
except requests.exceptions.RequestException as e:
|
|
436
|
+
print(f"Error checking name: {e}")
|
|
437
|
+
return None
|
|
438
|
+
|
|
403
439
|
print(f"Deploying '{self.name}'...")
|
|
404
440
|
|
|
405
441
|
payload = cloudpickle.dumps((self.func, args, {**kwargs, 'port': port}))
|
|
@@ -418,14 +454,22 @@ CMD ["python", "entrypoint.py"]
|
|
|
418
454
|
print("Uploading...")
|
|
419
455
|
with open(archive_path, 'rb') as f:
|
|
420
456
|
response = requests.post(
|
|
421
|
-
"
|
|
422
|
-
data={"function_name": self.name, "port": port},
|
|
457
|
+
f"{base_url}/v1/deploy",
|
|
458
|
+
data={"function_name": self.name, "port": port, "memory": memory},
|
|
423
459
|
files={'source_archive': (archive_name, f, 'application/gzip')},
|
|
424
460
|
headers={"X-API-Key": self.api_key},
|
|
425
461
|
timeout=9000,
|
|
426
462
|
stream=True,
|
|
427
463
|
)
|
|
428
464
|
|
|
465
|
+
if not response.ok:
|
|
466
|
+
print(f"Deploy failed: {response.status_code}")
|
|
467
|
+
try:
|
|
468
|
+
print(f" {response.json()['detail']}")
|
|
469
|
+
except (json.JSONDecodeError, KeyError):
|
|
470
|
+
print(f" {response.text}")
|
|
471
|
+
return None
|
|
472
|
+
|
|
429
473
|
# Parse NDJSON stream
|
|
430
474
|
url = None
|
|
431
475
|
for line in response.iter_lines(decode_unicode=True):
|
|
@@ -436,6 +480,7 @@ CMD ["python", "entrypoint.py"]
|
|
|
436
480
|
print(f" [{status}] {msg}")
|
|
437
481
|
if status == "DONE":
|
|
438
482
|
url = event.get("url")
|
|
483
|
+
print(f"Deployed: {url}")
|
|
439
484
|
elif status == "ERROR":
|
|
440
485
|
return None
|
|
441
486
|
return url
|
cycls-0.0.2.94/cycls/__init__.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
from . import function as _function_module
|
|
2
|
-
from .function import function, Function
|
|
3
|
-
from .app import app, App
|
|
4
|
-
|
|
5
|
-
def __getattr__(name):
|
|
6
|
-
if name in ("api_key", "base_url"):
|
|
7
|
-
return getattr(_function_module, name)
|
|
8
|
-
raise AttributeError(f"module 'cycls' has no attribute '{name}'")
|
|
9
|
-
|
|
10
|
-
def __setattr__(name, value):
|
|
11
|
-
if name in ("api_key", "base_url"):
|
|
12
|
-
setattr(_function_module, name, value)
|
|
13
|
-
else:
|
|
14
|
-
raise AttributeError(f"module 'cycls' has no attribute '{name}'")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|