flock-core 0.4.0b33__py3-none-any.whl → 0.4.0b35__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/__init__.py +27 -5
- flock/core/api/main.py +138 -39
- flock/core/util/spliter.py +139 -54
- flock/webapp/__init__.py +1 -0
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +270 -0
- flock/webapp/app/api/execution.py +173 -0
- flock/webapp/app/api/flock_management.py +102 -0
- flock/webapp/app/api/registry_viewer.py +30 -0
- flock/webapp/app/config.py +9 -0
- flock/webapp/app/main.py +571 -0
- flock/webapp/app/models_ui.py +7 -0
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/flock_service.py +291 -0
- flock/webapp/app/utils.py +85 -0
- flock/webapp/run.py +30 -0
- flock/webapp/static/css/custom.css +527 -0
- flock/webapp/templates/base.html +98 -0
- flock/webapp/templates/flock_editor.html +17 -0
- flock/webapp/templates/index.html +12 -0
- flock/webapp/templates/partials/_agent_detail_form.html +97 -0
- flock/webapp/templates/partials/_agent_list.html +24 -0
- flock/webapp/templates/partials/_agent_manager_view.html +15 -0
- flock/webapp/templates/partials/_agent_tools_checklist.html +14 -0
- flock/webapp/templates/partials/_create_flock_form.html +52 -0
- flock/webapp/templates/partials/_dashboard_flock_detail.html +18 -0
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +17 -0
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +29 -0
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +17 -0
- flock/webapp/templates/partials/_dynamic_input_form_content.html +22 -0
- flock/webapp/templates/partials/_execution_form.html +48 -0
- flock/webapp/templates/partials/_execution_view_container.html +19 -0
- flock/webapp/templates/partials/_flock_file_list.html +24 -0
- flock/webapp/templates/partials/_flock_properties_form.html +51 -0
- flock/webapp/templates/partials/_flock_upload_form.html +17 -0
- flock/webapp/templates/partials/_load_manage_view.html +88 -0
- flock/webapp/templates/partials/_registry_table.html +25 -0
- flock/webapp/templates/partials/_registry_viewer_content.html +47 -0
- flock/webapp/templates/partials/_results_display.html +35 -0
- flock/webapp/templates/partials/_sidebar.html +63 -0
- flock/webapp/templates/partials/_structured_data_view.html +40 -0
- flock/webapp/templates/registry_viewer.html +84 -0
- {flock_core-0.4.0b33.dist-info → flock_core-0.4.0b35.dist-info}/METADATA +1 -1
- {flock_core-0.4.0b33.dist-info → flock_core-0.4.0b35.dist-info}/RECORD +48 -8
- {flock_core-0.4.0b33.dist-info → flock_core-0.4.0b35.dist-info}/WHEEL +0 -0
- {flock_core-0.4.0b33.dist-info → flock_core-0.4.0b35.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.0b33.dist-info → flock_core-0.4.0b35.dist-info}/licenses/LICENSE +0 -0
flock/__init__.py
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
"""Flock package initialization."""
|
|
2
2
|
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
3
6
|
|
|
4
7
|
def main():
|
|
5
8
|
"""Main function."""
|
|
9
|
+
# Parse command line arguments
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="Flock - Declarative LLM Orchestration at Scale"
|
|
12
|
+
)
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"--web",
|
|
15
|
+
action="store_true",
|
|
16
|
+
help="Start the web interface instead of the CLI",
|
|
17
|
+
)
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
|
|
20
|
+
# If --web flag is provided, start the web server
|
|
21
|
+
if args.web:
|
|
22
|
+
from flock.webapp.run import main as run_webapp
|
|
23
|
+
|
|
24
|
+
run_webapp()
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
# Otherwise, run the CLI interface
|
|
6
28
|
import questionary
|
|
7
29
|
from rich.console import Console
|
|
8
30
|
from rich.panel import Panel
|
|
@@ -116,11 +138,11 @@ def main():
|
|
|
116
138
|
elif result == CLI_SETTINGS:
|
|
117
139
|
settings_editor()
|
|
118
140
|
elif result == CLI_START_WEB_SERVER:
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
|
|
141
|
+
# Start the web server
|
|
142
|
+
from flock.webapp.run import main as run_webapp
|
|
143
|
+
|
|
144
|
+
run_webapp()
|
|
145
|
+
break
|
|
124
146
|
elif result == CLI_NOTES:
|
|
125
147
|
load_release_notes()
|
|
126
148
|
elif result == CLI_EXIT:
|
flock/core/api/main.py
CHANGED
|
@@ -12,14 +12,56 @@ from flock.core.api.models import FlockBatchRequest
|
|
|
12
12
|
from flock.core.flock import Flock
|
|
13
13
|
from flock.core.logging.logging import get_logger
|
|
14
14
|
|
|
15
|
+
logger = get_logger("api.main")
|
|
16
|
+
|
|
15
17
|
from .endpoints import create_api_router
|
|
16
18
|
|
|
17
19
|
# Import components from the api package
|
|
18
20
|
from .run_store import RunStore
|
|
19
|
-
from .ui.routes import FASTHTML_AVAILABLE, create_ui_app
|
|
20
|
-
from .ui.utils import format_result_to_html, parse_input_spec # Import UI utils
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# Conditionally import for the new UI integration
|
|
23
|
+
NEW_UI_SERVICE_AVAILABLE = False
|
|
24
|
+
WEBAPP_FASTAPI_APP = None
|
|
25
|
+
try:
|
|
26
|
+
from flock.webapp.app.main import (
|
|
27
|
+
app as webapp_fastapi_app, # Import the FastAPI app instance
|
|
28
|
+
)
|
|
29
|
+
from flock.webapp.app.services.flock_service import (
|
|
30
|
+
set_current_flock_instance_programmatically,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
WEBAPP_FASTAPI_APP = webapp_fastapi_app
|
|
34
|
+
NEW_UI_SERVICE_AVAILABLE = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
logger.warning(
|
|
37
|
+
"New webapp components (flock.webapp.app.main:app or flock.webapp.app.services.flock_service) not found. "
|
|
38
|
+
"UI mode will fall back to old FastHTML UI if available."
|
|
39
|
+
)
|
|
40
|
+
# Fallback: Import old UI components if new one isn't available and create_ui is True
|
|
41
|
+
try:
|
|
42
|
+
from .ui.routes import FASTHTML_AVAILABLE, create_ui_app
|
|
43
|
+
|
|
44
|
+
if FASTHTML_AVAILABLE: # Only import utils if fasthtml is there
|
|
45
|
+
from .ui.utils import format_result_to_html, parse_input_spec
|
|
46
|
+
else:
|
|
47
|
+
# Define placeholders if fasthtml itself is not available
|
|
48
|
+
def parse_input_spec(*args, **kwargs):
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
def format_result_to_html(*args, **kwargs):
|
|
52
|
+
return ""
|
|
53
|
+
|
|
54
|
+
FASTHTML_AVAILABLE = False # Ensure it's false if import failed
|
|
55
|
+
|
|
56
|
+
except ImportError:
|
|
57
|
+
FASTHTML_AVAILABLE = False # Ensure it's defined as false
|
|
58
|
+
|
|
59
|
+
# Define placeholders if utils can't be imported
|
|
60
|
+
def parse_input_spec(*args, **kwargs):
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
def format_result_to_html(*args, **kwargs):
|
|
64
|
+
return ""
|
|
23
65
|
|
|
24
66
|
|
|
25
67
|
class FlockAPI:
|
|
@@ -390,62 +432,119 @@ class FlockAPI:
|
|
|
390
432
|
server_name: str = "Flock API",
|
|
391
433
|
create_ui: bool = False,
|
|
392
434
|
):
|
|
393
|
-
"""Start the API server,
|
|
435
|
+
"""Start the API server. If create_ui is True, it mounts the new webapp or the old FastHTML UI at the root."""
|
|
394
436
|
if create_ui:
|
|
395
|
-
if
|
|
396
|
-
logger.
|
|
397
|
-
"
|
|
437
|
+
if NEW_UI_SERVICE_AVAILABLE and WEBAPP_FASTAPI_APP:
|
|
438
|
+
logger.info(
|
|
439
|
+
f"Preparing to mount new Scoped Web UI at root for Flock: {self.flock.name}"
|
|
398
440
|
)
|
|
399
|
-
else:
|
|
400
|
-
logger.info("Attempting to create and mount FastHTML UI at /ui")
|
|
401
441
|
try:
|
|
402
|
-
#
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
self,
|
|
406
|
-
api_host=host,
|
|
407
|
-
api_port=port,
|
|
408
|
-
server_name=server_name,
|
|
442
|
+
# Set the flock instance for the webapp
|
|
443
|
+
set_current_flock_instance_programmatically(
|
|
444
|
+
self.flock,
|
|
445
|
+
f"{self.flock.name.replace(' ', '_').lower()}_api_scoped.flock",
|
|
409
446
|
)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
# Add root redirect only if UI was successfully mounted
|
|
414
|
-
@self.app.get(
|
|
415
|
-
"/",
|
|
416
|
-
include_in_schema=False,
|
|
417
|
-
response_class=RedirectResponse,
|
|
447
|
+
logger.info(
|
|
448
|
+
f"Flock '{self.flock.name}' set for the new web UI (now part of the main app)."
|
|
418
449
|
)
|
|
419
|
-
async def root_redirect():
|
|
420
|
-
logger.debug("Redirecting / to /ui/")
|
|
421
|
-
return "/ui/"
|
|
422
450
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
451
|
+
# Mount the new web UI app at the root of self.app
|
|
452
|
+
# The WEBAPP_FASTAPI_APP handles its own routes including '/', static files etc.
|
|
453
|
+
# It will need to be started with ui_mode=scoped, which should be handled by
|
|
454
|
+
# the client accessing /?ui_mode=scoped initially.
|
|
455
|
+
self.app.mount(
|
|
456
|
+
"/", WEBAPP_FASTAPI_APP, name="flock_ui_root"
|
|
457
|
+
)
|
|
458
|
+
logger.info(
|
|
459
|
+
f"New Web UI (scoped mode) mounted at root. Access at http://{host}:{port}/?ui_mode=scoped"
|
|
426
460
|
)
|
|
461
|
+
# No explicit root redirect needed from self.app to WEBAPP_FASTAPI_APP's root,
|
|
462
|
+
# as WEBAPP_FASTAPI_APP now *is* the handler for "/".
|
|
463
|
+
# The API's own routes (e.g. /api/...) will still be served by self.app if they don't conflict.
|
|
464
|
+
|
|
465
|
+
logger.info(
|
|
466
|
+
f"API server '{server_name}' (with integrated UI) starting on http://{host}:{port}"
|
|
467
|
+
)
|
|
468
|
+
logger.info(
|
|
469
|
+
f"Access the Scoped UI for '{self.flock.name}' at http://{host}:{port}/?ui_mode=scoped"
|
|
470
|
+
)
|
|
471
|
+
|
|
427
472
|
except Exception as e:
|
|
428
473
|
logger.error(
|
|
429
|
-
f"
|
|
474
|
+
f"Error setting up or mounting new scoped UI at root: {e}. "
|
|
475
|
+
"API will start, UI might be impacted.",
|
|
430
476
|
exc_info=True,
|
|
431
477
|
)
|
|
478
|
+
elif FASTHTML_AVAILABLE: # Fallback to old FastHTML UI
|
|
479
|
+
logger.warning(
|
|
480
|
+
"New webapp not available or WEBAPP_FASTAPI_APP is None. Falling back to old FastHTML UI (mounted at /ui)."
|
|
481
|
+
)
|
|
482
|
+
try:
|
|
483
|
+
from .ui.routes import create_ui_app
|
|
484
|
+
except ImportError:
|
|
485
|
+
logger.error(
|
|
486
|
+
"Failed to import create_ui_app for old UI. API running without UI."
|
|
487
|
+
)
|
|
488
|
+
FASTHTML_AVAILABLE = False
|
|
432
489
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
490
|
+
if FASTHTML_AVAILABLE:
|
|
491
|
+
logger.info(
|
|
492
|
+
"Attempting to create and mount old FastHTML UI at /ui"
|
|
493
|
+
) # Old UI stays at /ui
|
|
494
|
+
try:
|
|
495
|
+
fh_app = create_ui_app(
|
|
496
|
+
self,
|
|
497
|
+
api_host=host,
|
|
498
|
+
api_port=port,
|
|
499
|
+
server_name=server_name,
|
|
500
|
+
)
|
|
501
|
+
self.app.mount(
|
|
502
|
+
"/ui", fh_app, name="old_flock_ui"
|
|
503
|
+
) # Old UI still at /ui
|
|
504
|
+
logger.info(
|
|
505
|
+
"Old FastHTML UI mounted successfully at /ui."
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
@self.app.get(
|
|
509
|
+
"/",
|
|
510
|
+
include_in_schema=False,
|
|
511
|
+
response_class=RedirectResponse,
|
|
512
|
+
)
|
|
513
|
+
async def root_redirect_to_old_ui(): # Redirect / to /ui/ for old UI
|
|
514
|
+
logger.debug("Redirecting / to /ui/ (old UI)")
|
|
515
|
+
return RedirectResponse(url="/ui/", status_code=303)
|
|
516
|
+
|
|
517
|
+
logger.info(
|
|
518
|
+
f"Old FastHTML UI available at http://{host}:{port}/ui/"
|
|
519
|
+
)
|
|
520
|
+
except Exception as e:
|
|
521
|
+
logger.error(
|
|
522
|
+
f"An error occurred setting up the old FastHTML UI: {e}. Running API only.",
|
|
523
|
+
exc_info=True,
|
|
524
|
+
)
|
|
525
|
+
else:
|
|
526
|
+
logger.error(
|
|
527
|
+
"No UI components available. API running without UI."
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
if not create_ui:
|
|
531
|
+
logger.info(
|
|
532
|
+
f"API server '{server_name}' starting on http://{host}:{port} (UI not requested)."
|
|
439
533
|
)
|
|
534
|
+
elif (
|
|
535
|
+
not (NEW_UI_SERVICE_AVAILABLE and WEBAPP_FASTAPI_APP)
|
|
536
|
+
and not FASTHTML_AVAILABLE
|
|
440
537
|
):
|
|
441
|
-
logger.info(
|
|
538
|
+
logger.info(
|
|
539
|
+
f"API server '{server_name}' starting on http://{host}:{port}. UI was requested but no components found."
|
|
540
|
+
)
|
|
442
541
|
|
|
443
542
|
uvicorn.run(self.app, host=host, port=port)
|
|
444
543
|
|
|
445
544
|
async def stop(self):
|
|
446
545
|
"""Stop the API server."""
|
|
447
546
|
logger.info("Stopping API server (cleanup if necessary)")
|
|
448
|
-
pass
|
|
547
|
+
pass
|
|
449
548
|
|
|
450
549
|
|
|
451
550
|
# --- End of file ---
|
flock/core/util/spliter.py
CHANGED
|
@@ -2,46 +2,37 @@ import re
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def split_top_level(spec: str) -> list[str]:
|
|
5
|
-
"""
|
|
6
|
-
are **not** inside brackets, braces, parentheses, string literals,
|
|
7
|
-
or the free-form description).
|
|
8
|
-
|
|
9
|
-
Strategy
|
|
10
|
-
--------
|
|
11
|
-
1. Single pass, char-by-char.
|
|
12
|
-
2. Track • nesting depth for (), [], {}
|
|
13
|
-
• whether we are inside a quoted string
|
|
14
|
-
3. Consider a comma a separator *only* if depth == 0, we are not
|
|
15
|
-
inside quotes **and** the text that follows matches
|
|
16
|
-
`optional-ws + identifier + optional-ws + ':'`
|
|
17
|
-
which can only be the start of the next field.
|
|
18
|
-
"""
|
|
5
|
+
"""Return raw field strings, split on *top-level* commas."""
|
|
19
6
|
fields: list[str] = []
|
|
20
7
|
start = 0
|
|
21
8
|
depth = 0
|
|
22
9
|
quote_char: str | None = None
|
|
23
10
|
i = 0
|
|
24
|
-
ident_re = re.compile(r"[A-Za-z_]\w*") # cheap
|
|
11
|
+
ident_re = re.compile(r"[A-Za-z_]\w*") # cheap identifier
|
|
25
12
|
|
|
26
13
|
while i < len(spec):
|
|
27
14
|
ch = spec[i]
|
|
28
15
|
|
|
29
|
-
#
|
|
16
|
+
# ---------- string handling ----------
|
|
30
17
|
if quote_char:
|
|
18
|
+
if ch == "\\":
|
|
19
|
+
i += 2 # skip escaped char
|
|
20
|
+
continue
|
|
31
21
|
if ch == quote_char:
|
|
32
22
|
quote_char = None
|
|
33
23
|
i += 1
|
|
34
24
|
continue
|
|
35
25
|
|
|
36
26
|
if ch in {"'", '"'}:
|
|
37
|
-
# treat as string delimiter only in places regular Python would
|
|
38
27
|
prev = spec[i - 1] if i else " "
|
|
39
|
-
if
|
|
28
|
+
if (
|
|
29
|
+
depth or prev.isspace() or prev in "=([{,:"
|
|
30
|
+
): # looks like a quote
|
|
40
31
|
quote_char = ch
|
|
41
32
|
i += 1
|
|
42
33
|
continue
|
|
43
34
|
|
|
44
|
-
#
|
|
35
|
+
# ---------- bracket / brace / paren ----------
|
|
45
36
|
if ch in "([{":
|
|
46
37
|
depth += 1
|
|
47
38
|
i += 1
|
|
@@ -51,23 +42,28 @@ def split_top_level(spec: str) -> list[str]:
|
|
|
51
42
|
i += 1
|
|
52
43
|
continue
|
|
53
44
|
|
|
54
|
-
#
|
|
45
|
+
# ---------- field separators ----------
|
|
55
46
|
if ch == "," and depth == 0:
|
|
56
47
|
j = i + 1
|
|
57
48
|
while j < len(spec) and spec[j].isspace():
|
|
58
49
|
j += 1
|
|
59
|
-
if j
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
if j >= len(spec): # comma at end – split
|
|
51
|
+
fields.append(spec[start:i].strip())
|
|
52
|
+
start = i + 1
|
|
53
|
+
i += 1
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
id_match = ident_re.match(spec, j)
|
|
57
|
+
if id_match:
|
|
58
|
+
k = id_match.end()
|
|
59
|
+
while k < len(spec) and spec[k].isspace():
|
|
60
|
+
k += 1
|
|
61
|
+
if k >= len(spec) or spec[k] in {":", "|", ","}:
|
|
62
|
+
# confirmed: comma separates two fields
|
|
63
|
+
fields.append(spec[start:i].strip())
|
|
64
|
+
start = i + 1
|
|
65
|
+
i += 1
|
|
66
|
+
continue
|
|
71
67
|
|
|
72
68
|
i += 1
|
|
73
69
|
|
|
@@ -76,21 +72,29 @@ def split_top_level(spec: str) -> list[str]:
|
|
|
76
72
|
|
|
77
73
|
|
|
78
74
|
def parse_schema(spec: str) -> list[tuple[str, str, str]]:
|
|
79
|
-
"""Turn
|
|
75
|
+
"""Turn *spec* into a list of (name, python_type, description)."""
|
|
80
76
|
result: list[tuple[str, str, str]] = []
|
|
81
77
|
|
|
82
78
|
for field in split_top_level(spec):
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
name = ""
|
|
80
|
+
type_str = "str" # default type
|
|
81
|
+
description = ""
|
|
85
82
|
|
|
86
|
-
name_part,
|
|
87
|
-
name = name_part.strip()
|
|
88
|
-
|
|
89
|
-
type_part, _, desc_part = rest.partition("|")
|
|
90
|
-
type_str = type_part.strip()
|
|
83
|
+
name_part, _, desc_part = field.partition("|")
|
|
91
84
|
description = desc_part.strip()
|
|
85
|
+
main_part = name_part.strip()
|
|
86
|
+
|
|
87
|
+
if ":" in main_part:
|
|
88
|
+
name, type_candidate = main_part.split(":", 1)
|
|
89
|
+
name = name.strip()
|
|
90
|
+
type_candidate = type_candidate.strip()
|
|
91
|
+
if type_candidate:
|
|
92
|
+
type_str = type_candidate
|
|
93
|
+
else:
|
|
94
|
+
name = main_part # keeps default type
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
if name: # skip broken pieces
|
|
97
|
+
result.append((name, type_str, description))
|
|
94
98
|
|
|
95
99
|
return result
|
|
96
100
|
|
|
@@ -107,20 +111,101 @@ if __name__ == "__main__":
|
|
|
107
111
|
SAMPLE_2 = (
|
|
108
112
|
"field_with_internal_quotes: Literal['type_A', "
|
|
109
113
|
'"type_B_with_\'_apostrophe"] | A literal with mixed quotes,'
|
|
110
|
-
" another_field:
|
|
114
|
+
" another_field :str| A field with a description"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
SAMPLE_3 = (
|
|
118
|
+
"field_with_internal_quotes: Literal['type_A', "
|
|
119
|
+
'"type_B_with_\'_apostrophe"] | A literal with mixed quotes,'
|
|
120
|
+
" another_field | A field with a description"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
SAMPLE_4 = "input, query, output"
|
|
124
|
+
|
|
125
|
+
SAMPLE_5 = (
|
|
126
|
+
"name: str | The character's full name,"
|
|
127
|
+
"race: str | The character's fantasy race,"
|
|
128
|
+
"class: Literal['mage','thief'] | The character's profession, which can be either mage or thief,"
|
|
129
|
+
"background: str | A brief backstory for the character"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
SAMPLE_6 = (
|
|
133
|
+
"summary: str | A short blurb, e.g. key:value pairs that appear in logs"
|
|
111
134
|
)
|
|
135
|
+
# ➜ [('summary', 'str',
|
|
136
|
+
# 'A short blurb, e.g. key:value pairs that appear in logs')]
|
|
112
137
|
|
|
113
|
-
|
|
114
|
-
print(f"Sample 2: {SAMPLE_2}")
|
|
138
|
+
SAMPLE_7 = "path: str | The literal string 'C:\\\\Program Files\\\\My,App'"
|
|
115
139
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
print(split_sample_1)
|
|
119
|
-
for field in split_sample_1:
|
|
120
|
-
print(parse_schema(field))
|
|
140
|
+
# ➜ [('path', 'str',
|
|
141
|
+
# "The literal string 'C:\\Program Files\\My,App'")]
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
SAMPLE_8 = (
|
|
144
|
+
"transform: Callable[[int, str], bool] | Function that returns True on success,"
|
|
145
|
+
"retries: int | How many times to retry"
|
|
146
|
+
)
|
|
147
|
+
# ➜ ('transform', 'Callable[[int, str], bool]', 'Function that returns True on success')
|
|
148
|
+
# ('retries', 'int', 'How many times to retry')
|
|
149
|
+
|
|
150
|
+
SAMPLE_9 = (
|
|
151
|
+
r"regex: str | Pattern such as r'^[A-Z\|a-z]+$',"
|
|
152
|
+
"flags: int | re flags to use"
|
|
153
|
+
)
|
|
154
|
+
# ➜ ('regex', 'str', "Pattern such as r'^[A-Z\\|a-z]+$'")
|
|
155
|
+
# ('flags', 'int', 're flags to use')
|
|
156
|
+
|
|
157
|
+
SAMPLE_10 = "id:int, name:str," # note the final comma!
|
|
158
|
+
# ➜ ('id', 'int', '')
|
|
159
|
+
# ('name', 'str', '')
|
|
160
|
+
|
|
161
|
+
SAMPLE_11 = "id:int | Primary key\nname:str | Display name\nactive:bool"
|
|
162
|
+
# ➜ should not work!
|
|
163
|
+
|
|
164
|
+
SAMPLE_12 = (
|
|
165
|
+
'comment:str | The text "done | failed" goes here,'
|
|
166
|
+
'status:Literal["done","failed"]'
|
|
167
|
+
)
|
|
168
|
+
# ➜ ('comment', 'str', 'The text "done | failed" goes here')
|
|
169
|
+
# ('status', 'Literal["done","failed"]', '')
|
|
170
|
+
|
|
171
|
+
SAMPLE_13 = "choice: Literal['He said \\'yes\\'', 'no'] | User response"
|
|
172
|
+
# ➜ ('choice', "Literal['He said \\'yes\\'', 'no']", 'User response')
|
|
173
|
+
|
|
174
|
+
SAMPLE_14 = ""
|
|
175
|
+
# ➜ []
|
|
176
|
+
|
|
177
|
+
SAMPLE_15 = "username"
|
|
178
|
+
# ➜ [('username', 'str', '')]
|
|
179
|
+
|
|
180
|
+
SAMPLE_16 = (
|
|
181
|
+
"payload: dict[str, list[dict[str, tuple[int,str]]]] "
|
|
182
|
+
"| Arbitrarily complex structure"
|
|
183
|
+
)
|
|
184
|
+
# ➜ ('payload', 'dict[str, list[dict[str, tuple[int,str]]]]',
|
|
185
|
+
# 'Arbitrarily complex structure')
|
|
186
|
+
|
|
187
|
+
SAMPLE_17 = "münze: str | Deutsche Münzbezeichnung, engl. 'coin'"
|
|
188
|
+
# ➜ [('münze', 'str', "Deutsche Münzbezeichnung, engl. 'coin'")]
|
|
189
|
+
|
|
190
|
+
for title, spec in [
|
|
191
|
+
("Sample-1", SAMPLE_1),
|
|
192
|
+
("Sample-2", SAMPLE_2),
|
|
193
|
+
("Sample-3", SAMPLE_3),
|
|
194
|
+
("Sample-4", SAMPLE_4),
|
|
195
|
+
("Sample-5", SAMPLE_5),
|
|
196
|
+
("Sample-6", SAMPLE_6),
|
|
197
|
+
("Sample-7", SAMPLE_7),
|
|
198
|
+
("Sample-8", SAMPLE_8),
|
|
199
|
+
("Sample-9", SAMPLE_9),
|
|
200
|
+
("Sample-10", SAMPLE_10),
|
|
201
|
+
("Sample-11", SAMPLE_11),
|
|
202
|
+
("Sample-12", SAMPLE_12),
|
|
203
|
+
("Sample-13", SAMPLE_13),
|
|
204
|
+
("Sample-14", SAMPLE_14),
|
|
205
|
+
("Sample-15", SAMPLE_15),
|
|
206
|
+
("Sample-16", SAMPLE_16),
|
|
207
|
+
("Sample-17", SAMPLE_17),
|
|
208
|
+
]:
|
|
209
|
+
print(f"\n{title}")
|
|
210
|
+
for row in parse_schema(spec):
|
|
211
|
+
print(row)
|
flock/webapp/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Flock Web Application Package."""
|
|
File without changes
|
|
File without changes
|