flock-core 0.2.2__py3-none-any.whl → 0.2.4__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 CHANGED
@@ -1 +1,5 @@
1
1
  """Flock package initialization."""
2
+
3
+ from flock.config import TELEMETRY
4
+
5
+ tracer = TELEMETRY.setup_tracing()
flock/config.py ADDED
@@ -0,0 +1,29 @@
1
+ # flock/config.py
2
+ import os
3
+
4
+ from flock.core.logging.telemetry import TelemetryConfig
5
+
6
+ # -- Connection and External Service Configurations --
7
+ TEMPORAL_SERVER_URL = os.getenv("TEMPORAL_SERVER_URL", "localhost:7233")
8
+ DEFAULT_MODEL = os.getenv("DEFAULT_MODEL", "openai/gpt-4o")
9
+
10
+ # API Keys and related settings
11
+ TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "")
12
+ GITHUB_PAT = os.getenv("GITHUB_PAT", "")
13
+ GITHUB_REPO = os.getenv("GITHUB_REPO", "")
14
+ GITHUB_USERNAME = os.getenv("GITHUB_USERNAME", "")
15
+
16
+ # -- Debugging and Logging Configurations --
17
+ LOCAL_DEBUG = os.getenv("LOCAL_DEBUG", "0") == "1"
18
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "DEBUG")
19
+
20
+ OTL_SERVICE_NAME = os.getenv("OTL_SERVICE_NAME", "otel-flock")
21
+ JAEGER_ENDPOINT = os.getenv(
22
+ "JAEGER_ENDPOINT", "http://localhost:14268/api/traces"
23
+ ) # Default gRPC endpoint for Jaeger
24
+ JAEGER_TRANSPORT = os.getenv(
25
+ "JAEGER_TRANSPORT", "http"
26
+ ).lower() # Options: "grpc" or "http"
27
+
28
+
29
+ TELEMETRY = TelemetryConfig(OTL_SERVICE_NAME, JAEGER_ENDPOINT, JAEGER_TRANSPORT)
flock/core/__init__.py CHANGED
@@ -1 +1,6 @@
1
+ """This module contains the core classes of the flock package."""
1
2
 
3
+ from flock.core.flock import Flock
4
+ from flock.core.flock_agent import FlockAgent
5
+
6
+ __all__ = ["Flock", "FlockAgent"]
flock/core/flock.py CHANGED
@@ -15,14 +15,13 @@ from flock.core.flock_agent import FlockAgent
15
15
  from flock.core.logging.formatters.base_formatter import FormatterOptions
16
16
  from flock.core.logging.formatters.pprint_formatter import PrettyPrintFormatter
17
17
  from flock.core.logging.logging import get_logger
18
- from flock.core.logging.telemetry import setup_tracing
19
18
  from flock.core.registry.agent_registry import Registry
20
19
  from flock.core.util.cli_helper import display_banner
21
20
  from flock.core.util.input_resolver import top_level_to_keys
22
21
 
23
22
  T = TypeVar("T", bound=FlockAgent)
24
23
  logger = get_logger("flock")
25
- tracer = setup_tracing()
24
+
26
25
  tracer = trace.get_tracer(__name__)
27
26
 
28
27
 
@@ -1,15 +1,21 @@
1
+ import json
1
2
  from typing import Any
2
3
 
3
4
  from flock.core.logging.formatters.base_formatter import BaseFormatter
4
5
 
5
6
 
6
7
  class PrettyPrintFormatter(BaseFormatter):
7
- def display_result(self, result: dict[str, Any], agent_name: str, **kwargs) -> None:
8
+ def display_result(
9
+ self, result: dict[str, Any], agent_name: str, **kwargs
10
+ ) -> None:
8
11
  """Print an agent's result using Rich formatting."""
9
- from devtools import pprint
12
+ from rich.console import Console
13
+ from rich.json import JSON
14
+
15
+ console = Console()
10
16
 
11
- pprint(agent_name)
12
- pprint(result)
17
+ console.print(agent_name)
18
+ console.print(JSON(json.dumps(result)))
13
19
 
14
20
  def display_data(self, data: dict[str, Any], **kwargs) -> None:
15
21
  """Print an agent's result using Rich formatting."""
@@ -1,21 +1,109 @@
1
+ """This module sets up OpenTelemetry tracing for a service."""
2
+
1
3
  from opentelemetry import trace
2
- from opentelemetry.exporter.jaeger.thrift import JaegerExporter
3
4
  from opentelemetry.sdk.resources import Resource
4
5
  from opentelemetry.sdk.trace import TracerProvider
5
- from opentelemetry.sdk.trace.export import BatchSpanProcessor
6
+ from opentelemetry.sdk.trace.export import (
7
+ BatchSpanProcessor,
8
+ )
9
+
10
+ from flock.core.logging.telemetry_exporter.file_span import FileSpanExporter
11
+ from flock.core.logging.telemetry_exporter.sqllite_span import (
12
+ SQLiteSpanExporter,
13
+ )
14
+
15
+
16
+ class TelemetryConfig:
17
+ """This configuration class sets up OpenTelemetry tracing.
18
+
19
+ - Export spans to a Jaeger collector using gRPC.
20
+ - Write spans to a file.
21
+ - Save spans in a SQLite database.
22
+
23
+ Only exporters with a non-None configuration will be activated.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ service_name: str,
29
+ jaeger_endpoint: str = None,
30
+ jaeger_transport: str = "grpc",
31
+ file_export_path: str = None,
32
+ sqlite_db_path: str = None,
33
+ batch_processor_options: dict = None,
34
+ ):
35
+ """:param service_name: Name of your service.
36
+
37
+ :param jaeger_endpoint: The Jaeger collector gRPC endpoint (e.g., "localhost:14250").
38
+ :param file_export_path: If provided, spans will be written to this file.
39
+ :param sqlite_db_path: If provided, spans will be stored in this SQLite DB.
40
+ :param batch_processor_options: Dict of options for BatchSpanProcessor (e.g., {"max_export_batch_size": 10}).
41
+ """
42
+ self.service_name = service_name
43
+ self.jaeger_endpoint = jaeger_endpoint
44
+ self.jaeger_transport = jaeger_transport
45
+ self.file_export_path = file_export_path
46
+ self.sqlite_db_path = sqlite_db_path
47
+ self.batch_processor_options = batch_processor_options or {}
48
+
49
+ def setup_tracing(self):
50
+ # Create a Resource with the service name.
51
+ resource = Resource(attributes={"service.name": self.service_name})
52
+ provider = TracerProvider(resource=resource)
53
+ trace.set_tracer_provider(provider)
54
+
55
+ # List to collect our span processors.
56
+ span_processors = []
57
+
58
+ # If a Jaeger endpoint is specified, add the Jaeger gRPC exporter.
59
+ if self.jaeger_endpoint:
60
+ if self.jaeger_transport == "grpc":
61
+ from opentelemetry.exporter.jaeger.proto.grpc import (
62
+ JaegerExporter,
63
+ )
64
+
65
+ jaeger_exporter = JaegerExporter(
66
+ endpoint=self.jaeger_endpoint,
67
+ insecure=True,
68
+ )
69
+ elif self.jaeger_transport == "http":
70
+ from opentelemetry.exporter.jaeger.thrift import JaegerExporter
71
+
72
+ jaeger_exporter = JaegerExporter(
73
+ collector_endpoint=self.jaeger_endpoint,
74
+ )
75
+ else:
76
+ raise ValueError(
77
+ "Invalid JAEGER_TRANSPORT specified. Use 'grpc' or 'http'."
78
+ )
79
+
80
+ span_processors.append(
81
+ BatchSpanProcessor(
82
+ jaeger_exporter, **self.batch_processor_options
83
+ )
84
+ )
6
85
 
86
+ # If a file path is provided, add the custom file exporter.
87
+ if self.file_export_path:
88
+ file_exporter = FileSpanExporter(self.file_export_path)
89
+ span_processors.append(
90
+ BatchSpanProcessor(
91
+ file_exporter, **self.batch_processor_options
92
+ )
93
+ )
7
94
 
8
- def setup_tracing():
9
- resource = Resource(attributes={"service.name": "flock-agent-framework"})
10
- provider = TracerProvider(resource=resource)
11
- trace.set_tracer_provider(provider)
95
+ # If a SQLite database path is provided, add the custom SQLite exporter.
96
+ if self.sqlite_db_path:
97
+ sqlite_exporter = SQLiteSpanExporter(self.sqlite_db_path)
98
+ span_processors.append(
99
+ BatchSpanProcessor(
100
+ sqlite_exporter, **self.batch_processor_options
101
+ )
102
+ )
12
103
 
13
- # Create a Jaeger exporter
14
- jaeger_exporter = JaegerExporter(
15
- agent_host_name="localhost", # or the hostname where Jaeger is running
16
- agent_port=6831, # default Jaeger agent port
17
- )
104
+ # Register all span processors with the provider.
105
+ for processor in span_processors:
106
+ provider.add_span_processor(processor)
18
107
 
19
- provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
20
- tracer = trace.get_tracer(__name__)
21
- return tracer
108
+ # Return a tracer instance.
109
+ return trace.get_tracer(__name__)
@@ -0,0 +1,37 @@
1
+ import json
2
+
3
+ from opentelemetry.sdk.trace.export import (
4
+ SpanExporter,
5
+ SpanExportResult,
6
+ )
7
+
8
+
9
+ class FileSpanExporter(SpanExporter):
10
+ """A simple exporter that writes span data as JSON lines into a file."""
11
+
12
+ def __init__(self, file_path: str):
13
+ self.file_path = file_path
14
+
15
+ def export(self, spans) -> SpanExportResult:
16
+ try:
17
+ with open(self.file_path, "a") as f:
18
+ for span in spans:
19
+ # Create a dictionary representation of the span.
20
+ span_dict = {
21
+ "name": span.name,
22
+ "trace_id": format(span.context.trace_id, "032x"),
23
+ "span_id": format(span.context.span_id, "016x"),
24
+ "start_time": span.start_time,
25
+ "end_time": span.end_time,
26
+ "attributes": span.attributes,
27
+ "status": str(span.status),
28
+ }
29
+ f.write(json.dumps(span_dict) + "\n")
30
+ return SpanExportResult.SUCCESS
31
+ except Exception as e:
32
+ print("Error exporting spans to file:", e)
33
+ return SpanExportResult.FAILURE
34
+
35
+ def shutdown(self) -> None:
36
+ # Nothing special needed on shutdown.
37
+ pass
@@ -0,0 +1,68 @@
1
+ import json
2
+ import sqlite3
3
+
4
+ from opentelemetry.sdk.trace.export import (
5
+ SpanExporter,
6
+ SpanExportResult,
7
+ )
8
+
9
+
10
+ class SQLiteSpanExporter(SpanExporter):
11
+ """A custom exporter that writes span data into a SQLite database."""
12
+
13
+ def __init__(self, sqlite_db_path: str):
14
+ self.sqlite_db_path = sqlite_db_path
15
+ self.conn = sqlite3.connect(
16
+ self.sqlite_db_path, check_same_thread=False
17
+ )
18
+ self._create_table()
19
+
20
+ def _create_table(self):
21
+ cursor = self.conn.cursor()
22
+ cursor.execute(
23
+ """
24
+ CREATE TABLE IF NOT EXISTS spans (
25
+ id TEXT PRIMARY KEY,
26
+ name TEXT,
27
+ trace_id TEXT,
28
+ span_id TEXT,
29
+ start_time INTEGER,
30
+ end_time INTEGER,
31
+ attributes TEXT,
32
+ status TEXT
33
+ )
34
+ """
35
+ )
36
+ self.conn.commit()
37
+
38
+ def export(self, spans) -> SpanExportResult:
39
+ try:
40
+ cursor = self.conn.cursor()
41
+ for span in spans:
42
+ span_id = format(span.context.span_id, "016x")
43
+ trace_id = format(span.context.trace_id, "032x")
44
+ cursor.execute(
45
+ """
46
+ INSERT OR REPLACE INTO spans
47
+ (id, name, trace_id, span_id, start_time, end_time, attributes, status)
48
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
49
+ """,
50
+ (
51
+ span_id,
52
+ span.name,
53
+ trace_id,
54
+ span_id,
55
+ span.start_time,
56
+ span.end_time,
57
+ json.dumps(span.attributes),
58
+ str(span.status),
59
+ ),
60
+ )
61
+ self.conn.commit()
62
+ return SpanExportResult.SUCCESS
63
+ except Exception as e:
64
+ print("Error exporting spans to SQLite:", e)
65
+ return SpanExportResult.FAILURE
66
+
67
+ def shutdown(self) -> None:
68
+ self.conn.close()
@@ -1,6 +1,13 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
1
3
  from rich.console import Console
2
4
  from rich.syntax import Text
3
5
 
6
+ try:
7
+ __version__ = version("flock-core")
8
+ except PackageNotFoundError:
9
+ __version__ = "0.2.0"
10
+
4
11
  console = Console()
5
12
 
6
13
 
@@ -21,5 +28,5 @@ def display_banner():
21
28
  )
22
29
  console.print(banner_text)
23
30
  console.print(
24
- f"[bold]v0.2.1[/] - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
31
+ f"[bold]v{__version__}[/] - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
25
32
  )
@@ -96,7 +96,7 @@ async def run_agent(
96
96
  context.record(
97
97
  agent.name,
98
98
  result,
99
- timestamp=datetime.now(),
99
+ timestamp=datetime.now().isoformat(),
100
100
  hand_off=None,
101
101
  called_from=previous_agent_name,
102
102
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -15,6 +15,7 @@ Requires-Dist: httpx>=0.28.1
15
15
  Requires-Dist: loguru>=0.7.3
16
16
  Requires-Dist: msgpack>=1.1.0
17
17
  Requires-Dist: opentelemetry-api>=1.30.0
18
+ Requires-Dist: opentelemetry-exporter-jaeger-proto-grpc>=1.21.0
18
19
  Requires-Dist: opentelemetry-exporter-jaeger>=1.21.0
19
20
  Requires-Dist: opentelemetry-exporter-otlp>=1.30.0
20
21
  Requires-Dist: opentelemetry-instrumentation-logging>=0.51b0
@@ -35,7 +36,14 @@ Description-Content-Type: text/markdown
35
36
 
36
37
  <p align="center">
37
38
  <img src="docs/img/flock.png" width="600"><br>
38
- <h1 align="center">Flock<br></h1><br>
39
+ <img alt="Dynamic TOML Badge" src="https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fwhiteducksoftware%2Fflock%2Frefs%2Fheads%2Fbadges%2Fpyproject.toml%3Ftoken%3DGHSAT0AAAAAACVFDVNBU3S6HLJSC36P3YNQZ5LNPQQ&query=%24.project.version&style=for-the-badge&logo=pypi&logoSize=large&label=pip%20version&link=https%3A%2F%2Fpypi.org%2Fproject%2Fflock-core%2F">
40
+
41
+
42
+
43
+
44
+
45
+
46
+
39
47
 
40
48
 
41
49
  ## Overview
@@ -1,6 +1,7 @@
1
- flock/__init__.py,sha256=JeRpPR29wyqXe8El-Ug4v_Ibhf5GQm_i8yWMi0cUYz0,36
2
- flock/core/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
3
- flock/core/flock.py,sha256=BKuPrOVzudoqauokWyf59UXKlKZ3Qy9iKOm3ZaU_18E,9517
1
+ flock/__init__.py,sha256=uJxXAxt0def69cccAAdLjBxQOFXVRO72RE82hJnODSw,108
2
+ flock/config.py,sha256=hul_Vmgl8w5a_4YNfg3Mvll3iVEkjiR5At9D0WsVesw,1006
3
+ flock/core/__init__.py,sha256=0Xq_txurlxxjKGXjRn6GNJusGTiBcd7zw2eF0L7JyuU,183
4
+ flock/core/flock.py,sha256=Iw7Frmz2aZheApxi2KSjsX7gA8ZcemzXhfKXeQdtY0w,9438
4
5
  flock/core/flock_agent.py,sha256=59qQ7ohOy2lc1KjI6SV7IcrqYL86ofAhq32pZGgk6eA,27761
5
6
  flock/core/context/context.py,sha256=jH06w4C_O5CEL-YxjX_x_dmgLe9Rcllnn1Ebs0dvwaE,6171
6
7
  flock/core/context/context_manager.py,sha256=qMySVny_dbTNLh21RHK_YT0mNKIOrqJDZpi9ZVdBsxU,1103
@@ -9,20 +10,22 @@ flock/core/execution/local_executor.py,sha256=O_dgQ_HJPCp97ghdEoDSNDIiaYkogrUS0G
9
10
  flock/core/execution/temporal_executor.py,sha256=ai6ikr9rEiN2Kc-208OalxtfqL_FTt_UaH6a--oEkJM,2010
10
11
  flock/core/logging/__init__.py,sha256=Q8hp9-1ilPIUIV0jLgJ3_cP7COrea32cVwL7dicPnlM,82
11
12
  flock/core/logging/logging.py,sha256=F-FDz9etBXmAIT-fjx3pUfvNXsckgR6ONCMaFZIc4Kw,4619
12
- flock/core/logging/telemetry.py,sha256=K7uhMQHf1aGvfeNnuAM-wd65tpal9ch5piYF6RxU4Js,786
13
+ flock/core/logging/telemetry.py,sha256=T2CRSiqOWvOsXe-WRsObkkOkrrd6z-BwEYLaBUU2AAM,4017
13
14
  flock/core/logging/trace_and_logged.py,sha256=h4YH8s0KjK4tiBdrEZdCLd4fDzMB5-NKwqzrtkWhQw4,1999
14
15
  flock/core/logging/formatters/base_formatter.py,sha256=CyG-X2NWq8sqEhFEO2aG7Mey5tVkIzoWiihW301_VIo,1023
15
16
  flock/core/logging/formatters/formatter_factory.py,sha256=hmH-NpCESHkioX0GBQ5CuQR4axyIXnSRWwAZCHylx6Q,1283
16
- flock/core/logging/formatters/pprint_formatter.py,sha256=2CZoMbfzJB9Xp9WaItxnXY3hfANMjDTXowk3ni6N-h4,565
17
+ flock/core/logging/formatters/pprint_formatter.py,sha256=tTm2WhwlCw-SX2Ouci5I9U_HVgxNGY5SSnzB9HZh8bg,692
17
18
  flock/core/logging/formatters/rich_formatters.py,sha256=h1FD0_cIdQBQ8P2x05XhgD1cmmP80IBNVT5jb3cAV9M,4776
18
19
  flock/core/logging/formatters/theme_builder.py,sha256=1RUEwPIDfCjwTapbK1liasA5SdukOn7YwbZ4H4j1WkI,17364
19
20
  flock/core/logging/formatters/themed_formatter.py,sha256=CbxmqUC7zkLzyIxngk-3dcpQ6vxPR6zaDNA2TAMitCI,16714
21
+ flock/core/logging/telemetry_exporter/file_span.py,sha256=e4hr4D7tC9j4KT7JZBuZU0YxQCdHKADpXNeUNEwggN4,1294
22
+ flock/core/logging/telemetry_exporter/sqllite_span.py,sha256=9bqxHt1mDQGyhKxA9dON5xDi_6FMOfBSdrU_zWV0xv4,2107
20
23
  flock/core/mixin/dspy_integration.py,sha256=oT5YfXxPhHkMCuwhXoppBAYBGePUAKse7KebGSM-bq0,6880
21
24
  flock/core/mixin/prompt_parser.py,sha256=eOqI-FK3y17gVqpc_y5GF-WmK1Jv8mFlkZxTcgweoxI,5121
22
25
  flock/core/registry/agent_registry.py,sha256=QHdr3Cb-32PEdz8jFCIZSH9OlfpRwAJMtSRpHCWJDq4,4889
23
26
  flock/core/tools/basic_tools.py,sha256=nRc1bIz96z-WUTe_yYf9V6EfCPEncl_XnrpGdC7dEmo,8721
24
27
  flock/core/tools/dev_tools/github.py,sha256=6ya2_eN-qITV3b_pYP24jQC3X4oZbRY5GKh1AF-9Zic,6836
25
- flock/core/util/cli_helper.py,sha256=hXBXhTQizZILXiVeBFR6QdAsQm0Qj29ANHJRgiekwyw,859
28
+ flock/core/util/cli_helper.py,sha256=aHLKjl5JBLIczLzjYeUcGQlVQRlypunxV2TYeAFX0KE,1030
26
29
  flock/core/util/input_resolver.py,sha256=OesGqX2Dld8myL9Qz04mmxLqoYqOSQC632pj1EMk9Yk,5456
27
30
  flock/core/util/serializable.py,sha256=SymJ0YrjBx48mOBItYSqoRpKuzIc4vKWRS6ScTzre7s,2573
28
31
  flock/themes/3024-day.toml,sha256=uOVHqEzSyHx0WlUk3D0lne4RBsNBAPCTy3C58yU7kEY,667
@@ -362,11 +365,11 @@ flock/themes/zenburned.toml,sha256=UEmquBbcAO3Zj652XKUwCsNoC2iQSlIh-q5c6DH-7Kc,1
362
365
  flock/themes/zenwritten-dark.toml,sha256=To5l6520_3UqAGiEumpzGWsHhXxqu9ThrMildXKgIO0,1669
363
366
  flock/themes/zenwritten-light.toml,sha256=G1iEheCPfBNsMTGaVpEVpDzYBHA_T-MV27rolUYolmE,1666
364
367
  flock/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
365
- flock/workflow/activities.py,sha256=G3LcW0Bs0-Iu_ry0Ch8DbP8yJ1eftwaZZ71PVWJaoVs,7634
368
+ flock/workflow/activities.py,sha256=YEg-Gr8kzVsxWsmsZguIVhX2XwMRvhZ2OlnsJoG5g_A,7646
366
369
  flock/workflow/agent_activities.py,sha256=NhBZscflEf2IMfSRa_pBM_TRP7uVEF_O0ROvWZ33eDc,963
367
370
  flock/workflow/temporal_setup.py,sha256=VWBgmBgfTBjwM5ruS_dVpA5AVxx6EZ7oFPGw4j3m0l0,1091
368
371
  flock/workflow/workflow.py,sha256=I9MryXW_bqYVTHx-nl2epbTqeRy27CAWHHA7ZZA0nAk,1696
369
- flock_core-0.2.2.dist-info/METADATA,sha256=P_6coaQ37bbmA6pbqptH8N5EvDHqroC_shFqAChW1_A,11065
370
- flock_core-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
371
- flock_core-0.2.2.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
372
- flock_core-0.2.2.dist-info/RECORD,,
372
+ flock_core-0.2.4.dist-info/METADATA,sha256=VztTdTQKj4pIiPnSearGjlMDmPA_1o0Nak_EefDzs4o,11488
373
+ flock_core-0.2.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
374
+ flock_core-0.2.4.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
375
+ flock_core-0.2.4.dist-info/RECORD,,