codegraphcontext 0.1.9__tar.gz → 0.1.10__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.
Files changed (31) hide show
  1. {codegraphcontext-0.1.9/src/codegraphcontext.egg-info → codegraphcontext-0.1.10}/PKG-INFO +12 -3
  2. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/README.md +11 -2
  3. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/pyproject.toml +1 -1
  4. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/cli/main.py +77 -35
  5. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/core/database.py +30 -7
  6. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/core/jobs.py +24 -13
  7. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/core/watcher.py +53 -25
  8. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/prompts.py +7 -1
  9. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/server.py +102 -30
  10. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/tools/graph_builder.py +1 -1
  11. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/tools/import_extractor.py +44 -17
  12. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10/src/codegraphcontext.egg-info}/PKG-INFO +12 -3
  13. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/LICENSE +0 -0
  14. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/setup.cfg +0 -0
  15. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/__init__.py +0 -0
  16. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/__main__.py +0 -0
  17. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/cli/__init__.py +0 -0
  18. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/cli/setup_wizard.py +0 -0
  19. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/core/__init__.py +0 -0
  20. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/tools/__init__.py +0 -0
  21. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/tools/code_finder.py +0 -0
  22. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext/tools/system.py +0 -0
  23. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext.egg-info/SOURCES.txt +0 -0
  24. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
  25. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
  26. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext.egg-info/requires.txt +0 -0
  27. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/src/codegraphcontext.egg-info/top_level.txt +0 -0
  28. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/tests/test_cgc_integration.py +0 -0
  29. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/tests/test_imports.py +0 -0
  30. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/tests/test_jsonrpc.py +0 -0
  31. {codegraphcontext-0.1.9 → codegraphcontext-0.1.10}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: An MCP server that indexes local code into a graph database to provide context to AI assistants.
5
5
  Author-email: Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
6
6
  License: MIT License
@@ -60,9 +60,10 @@ Dynamic: license-file
60
60
  An MCP server that indexes local code into a graph database to provide context to AI assistants.
61
61
 
62
62
  ## Project Details
63
- - **Version:** 0.1.8
63
+ - **Version:** 0.1.10
64
64
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
65
65
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
66
+ - **Website:** [CodeGraphContext](http://code-graph-context.vercel.app/)
66
67
 
67
68
  ## Features
68
69
 
@@ -96,8 +97,15 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
96
97
 
97
98
  1. **Install:** `pip install codegraphcontext`
98
99
  2. **Setup:** `cgc setup`
100
+ This interactive command guides you through configuring your Neo4j database connection. It offers several options:
101
+ * **Local Setup (Docker Recommended):** Helps you set up a local Neo4j instance using Docker, which is the easiest way to get started.
102
+ * **Local Setup (Linux Binary):** For Debian-based Linux systems (like Ubuntu), `cgc setup` can automate the installation of Neo4j directly on your machine.
103
+ * **Hosted Setup:** Allows you to connect to an existing remote Neo4j database (e.g., Neo4j AuraDB).
104
+ Upon successful configuration, `cgc setup` will generate two important files:
105
+ * `mcp.json`: Contains the MCP client configuration for CodeGraphContext.
106
+ * `~/.codegraphcontext/.env`: Stores your Neo4j connection credentials securely.
99
107
  3. **Start:** `cgc start`
100
- 4. **Index Code:** `cgc tool add-code-to-graph '{"path": "/path/to/your/project"}'` (Under active development)
108
+
101
109
 
102
110
  ## MCP Client Configuration
103
111
 
@@ -205,6 +213,7 @@ Once the server is running, you can interact with it through your AI assistant u
205
213
  ## Contributing
206
214
 
207
215
  Contributions are welcome! 🎉
216
+ Please see our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
208
217
  If you have ideas for new features, integrations, or improvements, open an [issue](https://github.com/Shashankss1205/CodeGraphContext/issues) or submit a PR.
209
218
 
210
219
  Join discussions and help shape the future of CodeGraphContext.
@@ -8,9 +8,10 @@
8
8
  An MCP server that indexes local code into a graph database to provide context to AI assistants.
9
9
 
10
10
  ## Project Details
11
- - **Version:** 0.1.8
11
+ - **Version:** 0.1.10
12
12
  - **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
13
13
  - **License:** MIT License (See [LICENSE](LICENSE) for details)
14
+ - **Website:** [CodeGraphContext](http://code-graph-context.vercel.app/)
14
15
 
15
16
  ## Features
16
17
 
@@ -44,8 +45,15 @@ If you’re using CodeGraphContext in your project, feel free to open a PR and a
44
45
 
45
46
  1. **Install:** `pip install codegraphcontext`
46
47
  2. **Setup:** `cgc setup`
48
+ This interactive command guides you through configuring your Neo4j database connection. It offers several options:
49
+ * **Local Setup (Docker Recommended):** Helps you set up a local Neo4j instance using Docker, which is the easiest way to get started.
50
+ * **Local Setup (Linux Binary):** For Debian-based Linux systems (like Ubuntu), `cgc setup` can automate the installation of Neo4j directly on your machine.
51
+ * **Hosted Setup:** Allows you to connect to an existing remote Neo4j database (e.g., Neo4j AuraDB).
52
+ Upon successful configuration, `cgc setup` will generate two important files:
53
+ * `mcp.json`: Contains the MCP client configuration for CodeGraphContext.
54
+ * `~/.codegraphcontext/.env`: Stores your Neo4j connection credentials securely.
47
55
  3. **Start:** `cgc start`
48
- 4. **Index Code:** `cgc tool add-code-to-graph '{"path": "/path/to/your/project"}'` (Under active development)
56
+
49
57
 
50
58
  ## MCP Client Configuration
51
59
 
@@ -153,6 +161,7 @@ Once the server is running, you can interact with it through your AI assistant u
153
161
  ## Contributing
154
162
 
155
163
  Contributions are welcome! 🎉
164
+ Please see our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
156
165
  If you have ideas for new features, integrations, or improvements, open an [issue](https://github.com/Shashankss1205/CodeGraphContext/issues) or submit a PR.
157
166
 
158
167
  Join discussions and help shape the future of CodeGraphContext.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codegraphcontext"
3
- version = "0.1.9"
3
+ version = "0.1.10"
4
4
  description = "An MCP server that indexes local code into a graph database to provide context to AI assistants."
5
5
  authors = [{ name = "Shashank Shekhar Singh", email = "shashankshekharsingh1205@gmail.com" }]
6
6
  readme = "README.md"
@@ -1,4 +1,15 @@
1
1
  # src/codegraphcontext/cli/main.py
2
+ """
3
+ This module defines the command-line interface (CLI) for the CodeGraphContext application.
4
+ It uses the Typer library to create a user-friendly and well-documented CLI.
5
+
6
+ Commands:
7
+ - setup: Runs an interactive wizard to configure the Neo4j database connection.
8
+ - start: Launches the main MCP server.
9
+ - tool: A placeholder for directly calling server tools (for debugging).
10
+ - help: Displays help information.
11
+ - version: Show the installed version.
12
+ """
2
13
  import typer
3
14
  from rich.console import Console
4
15
  import asyncio
@@ -7,12 +18,14 @@ import json
7
18
  import os
8
19
  from pathlib import Path
9
20
  from dotenv import load_dotenv, find_dotenv
21
+ from importlib.metadata import version as pkg_version, PackageNotFoundError
10
22
  from codegraphcontext.server import MCPServer
11
23
  from .setup_wizard import run_setup_wizard
12
24
 
13
- # Set the log level for the noisy neo4j logger to WARNING
14
- logging.getLogger("neo4j").setLevel(logging.WARNING) # <-- ADD THIS LINE
25
+ # Set the log level for the noisy neo4j logger to WARNING to keep the output clean.
26
+ logging.getLogger("neo4j").setLevel(logging.WARNING)
15
27
 
28
+ # Initialize the Typer app and Rich console for formatted output.
16
29
  app = typer.Typer(
17
30
  name="cgc",
18
31
  help="CodeGraphContext: An MCP server for AI-powered code analysis.",
@@ -20,29 +33,49 @@ app = typer.Typer(
20
33
  )
21
34
  console = Console()
22
35
 
36
+ # Configure basic logging for the application.
23
37
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
24
38
 
39
+
40
+ def get_version() -> str:
41
+ """
42
+ Try to read version from the installed package metadata.
43
+ Fallback to a dev version if not installed.
44
+ """
45
+ try:
46
+ return pkg_version("codegraphcontext") # must match [project].name in pyproject.toml
47
+ except PackageNotFoundError:
48
+ return "0.0.0 (dev)"
49
+
50
+
25
51
  @app.command()
26
52
  def setup():
27
53
  """
28
- Run the interactive setup wizard to configure the server and database.
54
+ Runs the interactive setup wizard to configure the server and database connection.
55
+ This helps users set up a local Docker-based Neo4j instance or connect to a remote one.
29
56
  """
30
57
  run_setup_wizard()
31
58
 
32
59
  @app.command()
33
60
  def start():
34
61
  """
35
- Start the CodeGraphContext MCP server.
62
+ Starts the CodeGraphContext MCP server, which listens for JSON-RPC requests from stdin.
63
+ It first attempts to load Neo4j credentials from various sources before starting.
36
64
  """
37
65
  console.print("[bold green]Starting CodeGraphContext Server...[/bold green]")
38
-
39
- # 1. Prefer loading environment variables from mcp.json in the current directory
66
+
67
+ # The server needs Neo4j credentials. It attempts to load them in the following order of priority:
68
+ # 1. From a local `mcp.json` file in the current working directory.
69
+ # 2. From a global `.env` file at `~/.codegraphcontext/.env`.
70
+ # 3. From any `.env` file found by searching upwards from the current directory.
71
+
72
+ # 1. Prefer loading environment variables from mcp.json in the current directory.
40
73
  mcp_file_path = Path.cwd() / "mcp.json"
41
74
  if mcp_file_path.exists():
42
75
  try:
43
76
  with open(mcp_file_path, "r") as f:
44
77
  mcp_config = json.load(f)
45
-
78
+
46
79
  server_env = mcp_config.get("mcpServers", {}).get("CodeGraphContext", {}).get("env", {})
47
80
  for key, value in server_env.items():
48
81
  os.environ[key] = value
@@ -51,7 +84,7 @@ def start():
51
84
  console.print(f"[bold red]Error loading mcp.json:[/bold red] {e}")
52
85
  console.print("[yellow]Attempting to start server without mcp.json environment variables.[/yellow]")
53
86
  else:
54
- # 2. If no local mcp.json, try to load from ~/.codegraphcontext/.env
87
+ # 2. If no local mcp.json, try to load from the global config directory.
55
88
  global_env_path = Path.home() / ".codegraphcontext" / ".env"
56
89
  if global_env_path.exists():
57
90
  try:
@@ -61,30 +94,34 @@ def start():
61
94
  console.print(f"[bold red]Error loading global .env file from {global_env_path}:[/bold red] {e}")
62
95
  console.print("[yellow]Attempting to start server without .env environment variables.[/yellow]")
63
96
  else:
64
- # Fallback: try to load from a .env file found by find_dotenv (searches up the tree)
97
+ # 3. Fallback: try to load from any .env file found by searching up the directory tree.
65
98
  try:
66
99
  dotenv_path = find_dotenv(usecwd=True, raise_error_if_not_found=False)
67
100
  if dotenv_path:
68
101
  load_dotenv(dotenv_path)
69
- console.print(f"[green]Loaded Neo4j credentials from global .env file: {dotenv_path}[/green]")
102
+ console.print(f"[green]Loaded Neo4j credentials from discovered .env file: {dotenv_path}[/green]")
70
103
  else:
71
104
  console.print("[yellow]No local mcp.json or global .env file found. Attempting to start server without explicit Neo4j credentials.[/yellow]")
72
105
  except Exception as e:
73
- console.print(f"[bold red]Error loading global .env file:[/bold red] {e}")
106
+ console.print(f"[bold red]Error loading .env file:[/bold red] {e}")
74
107
  console.print("[yellow]Attempting to start server without .env environment variables.[/yellow]")
75
108
 
76
109
  server = None
77
110
  loop = asyncio.new_event_loop()
78
111
  asyncio.set_event_loop(loop)
79
112
  try:
113
+ # Initialize and run the main server.
80
114
  server = MCPServer(loop=loop)
81
115
  loop.run_until_complete(server.run())
82
116
  except ValueError as e:
117
+ # This typically happens if credentials are still not found after all checks.
83
118
  console.print(f"[bold red]Configuration Error:[/bold red] {e}")
84
119
  console.print("Please run `cgc setup` to configure the server.")
85
120
  except KeyboardInterrupt:
121
+ # Handle graceful shutdown on Ctrl+C.
86
122
  console.print("\n[bold yellow]Server stopped by user.[/bold yellow]")
87
123
  finally:
124
+ # Ensure server and event loop are properly closed.
88
125
  if server:
89
126
  server.shutdown()
90
127
  loop.close()
@@ -96,46 +133,51 @@ def tool(
96
133
  args: str = typer.Argument("{}", help="A JSON string of arguments for the tool."),
97
134
  ):
98
135
  """
99
- Directly call a CodeGraphContext tool.
100
- Note: This command instantiates a new, independent MCP server instance for each call.
101
- Therefore, it does not share state (like job IDs) with a server started via `cgc start`.
102
-
103
- This command can be used for:\n
104
- - `add_code_to_graph`: Index a new project or directory. Args: `path` (str), `is_dependency` (bool, optional)\n
105
- - `add_package_to_graph`: Add a Python package to the graph. Args: `package_name` (str), `is_dependency` (bool, optional)\n
106
- - `find_code`: Search for code snippets. Args: `query` (str)\n
107
- - `analyze_code_relationships`: Analyze code relationships (e.g., callers, callees). Args: `query_type` (str), `target` (str), `context` (str, optional)\n
108
- - `watch_directory`: Start watching a directory for changes. Args: `path` (str)\n
109
- - `execute_cypher_query`: Run direct Cypher queries. Args: `cypher_query` (str)\n
110
- - `list_imports`: List imports from files. Args: `path` (str), `language` (str, optional), `recursive` (bool, optional)\n
111
- - `find_dead_code`: Find potentially unused functions. Args: None\n
112
- - `calculate_cyclomatic_complexity`: Calculate function complexity. Args: `function_name` (str), `file_path` (str, optional)\n
113
- - `find_most_complex_functions`: Find the most complex functions. Args: `limit` (int, optional)\n
114
- - `list_indexed_repositories`: List indexed repositories. Args: None\n
115
- - `delete_repository`: Delete an indexed repository. Args: `repo_path` (str)
116
- """
136
+ Directly call a CodeGraphContext tool from the command line.
117
137
 
118
- # This is a placeholder for a more advanced tool caller that would
119
- # connect to the running server via a different mechanism (e.g., a socket).
120
- # For now, it's a conceptual part of the CLI.
138
+ IMPORTANT: This is a placeholder for debugging and does not connect to a running
139
+ server. It creates a new, temporary server instance for each call, so it cannot
140
+ be used to check the status of jobs started by `cgc start`.
141
+ """
121
142
  console.print(f"Calling tool [bold cyan]{name}[/bold cyan] with args: {args}")
122
143
  console.print("[yellow]Note: This is a placeholder for direct tool invocation.[/yellow]")
123
144
 
124
145
  @app.command()
125
146
  def help(ctx: typer.Context):
126
- """Show this message and exit."""
147
+ """Show the main help message and exit."""
127
148
  root_ctx = ctx.parent or ctx
128
149
  typer.echo(root_ctx.get_help())
129
150
 
130
151
 
152
+ @app.command("version")
153
+ def version_cmd():
154
+ """Show the application version."""
155
+ console.print(f"CodeGraphContext [bold cyan]{get_version()}[/bold cyan]")
156
+
157
+
131
158
  @app.callback(invoke_without_command=True)
132
- def main(ctx: typer.Context):
159
+ def main(
160
+ ctx: typer.Context,
161
+ version_: bool = typer.Option(
162
+ None,
163
+ "--version",
164
+ "-v",
165
+ help="Show the application version and exit.",
166
+ is_eager=True,
167
+ ),
168
+ ):
133
169
  """
134
- CodeGraphContext: An MCP server for AI-powered code analysis.
170
+ Main entry point for the cgc CLI application.
171
+ If no subcommand is provided, it displays a welcome message with instructions.
135
172
  """
173
+ if version_:
174
+ console.print(f"CodeGraphContext [bold cyan]{get_version()}[/bold cyan]")
175
+ raise typer.Exit()
176
+
136
177
  if ctx.invoked_subcommand is None:
137
178
  console.print("[bold green]👋 Welcome to CodeGraphContext (cgc)![/bold green]\n")
138
179
  console.print("👉 Run [cyan]cgc setup[/cyan] to configure the server and database.")
139
180
  console.print("👉 Run [cyan]cgc start[/cyan] to launch the server.")
140
181
  console.print("👉 Run [cyan]cgc help[/cyan] to see all available commands.\n")
182
+ console.print("👉 Run [cyan]cgc --version[/cyan] to check the version.\n")
141
183
  console.print("👉 Running [green]codegraphcontext [white]works the same as using [green]cgc")
@@ -1,4 +1,7 @@
1
1
  # src/codegraphcontext/core/database.py
2
+ """
3
+ This module provides a thread-safe singleton manager for the Neo4j database connection.
4
+ """
2
5
  import os
3
6
  import logging
4
7
  import threading
@@ -10,20 +13,30 @@ logger = logging.getLogger(__name__)
10
13
 
11
14
  class DatabaseManager:
12
15
  """
13
- Singleton class to manage Neo4j database connections.
16
+ Manages the Neo4j database driver as a singleton to ensure only one
17
+ connection pool is created and shared across the application.
18
+
19
+ This pattern is crucial for performance and resource management in a
20
+ multi-threaded or asynchronous application.
14
21
  """
15
22
  _instance = None
16
23
  _driver: Optional[Driver] = None
17
- _lock = threading.Lock()
24
+ _lock = threading.Lock() # Lock to ensure thread-safe initialization.
18
25
 
19
26
  def __new__(cls):
27
+ """Standard singleton pattern implementation."""
20
28
  if cls._instance is None:
21
29
  with cls._lock:
30
+ # Double-check locking to prevent race conditions.
22
31
  if cls._instance is None:
23
32
  cls._instance = super(DatabaseManager, cls).__new__(cls)
24
33
  return cls._instance
25
34
 
26
35
  def __init__(self):
36
+ """
37
+ Initializes the manager by reading credentials from environment variables.
38
+ The `_initialized` flag prevents re-initialization on subsequent calls.
39
+ """
27
40
  if hasattr(self, '_initialized'):
28
41
  return
29
42
 
@@ -33,10 +46,20 @@ class DatabaseManager:
33
46
  self._initialized = True
34
47
 
35
48
  def get_driver(self) -> Driver:
36
- """Get the Neo4j driver instance"""
49
+ """
50
+ Gets the Neo4j driver instance, creating it if it doesn't exist.
51
+ This method is thread-safe.
52
+
53
+ Raises:
54
+ ValueError: If Neo4j credentials are not set in environment variables.
55
+
56
+ Returns:
57
+ The active Neo4j Driver instance.
58
+ """
37
59
  if self._driver is None:
38
60
  with self._lock:
39
61
  if self._driver is None:
62
+ # Ensure all necessary credentials are provided.
40
63
  if not all([self.neo4j_uri, self.neo4j_username, self.neo4j_password]):
41
64
  raise ValueError(
42
65
  "Neo4j credentials must be set via environment variables:\n"
@@ -50,7 +73,7 @@ class DatabaseManager:
50
73
  self.neo4j_uri,
51
74
  auth=(self.neo4j_username, self.neo4j_password)
52
75
  )
53
- # Test the connection
76
+ # Test the connection immediately to fail fast if credentials are wrong.
54
77
  try:
55
78
  with self._driver.session() as session:
56
79
  session.run("RETURN 1").consume()
@@ -64,7 +87,7 @@ class DatabaseManager:
64
87
  return self._driver
65
88
 
66
89
  def close_driver(self):
67
- """Close the Neo4j driver"""
90
+ """Closes the Neo4j driver connection if it exists."""
68
91
  if self._driver is not None:
69
92
  with self._lock:
70
93
  if self._driver is not None:
@@ -73,7 +96,7 @@ class DatabaseManager:
73
96
  self._driver = None
74
97
 
75
98
  def is_connected(self) -> bool:
76
- """Check if connected to database"""
99
+ """Checks if the database connection is currently active."""
77
100
  if self._driver is None:
78
101
  return False
79
102
  try:
@@ -81,4 +104,4 @@ class DatabaseManager:
81
104
  session.run("RETURN 1").consume()
82
105
  return True
83
106
  except Exception:
84
- return False
107
+ return False
@@ -1,4 +1,8 @@
1
1
  # src/codegraphcontext/core/jobs.py
2
+ """
3
+ This module defines the data structures and manager for handling long-running,
4
+ background jobs, such as code indexing.
5
+ """
2
6
  import uuid
3
7
  import threading
4
8
  from datetime import datetime, timedelta
@@ -9,7 +13,7 @@ from pathlib import Path
9
13
 
10
14
 
11
15
  class JobStatus(Enum):
12
- """Job status enumeration"""
16
+ """Enumeration for the possible statuses of a background job."""
13
17
  PENDING = "pending"
14
18
  RUNNING = "running"
15
19
  COMPLETED = "completed"
@@ -18,7 +22,10 @@ class JobStatus(Enum):
18
22
 
19
23
  @dataclass
20
24
  class JobInfo:
21
- """Data class for job information"""
25
+ """
26
+ A data class to hold all information about a single background job.
27
+ This makes it easy to track the job's progress, status, and results.
28
+ """
22
29
  job_id: str
23
30
  status: JobStatus
24
31
  start_time: datetime
@@ -34,19 +41,20 @@ class JobInfo:
34
41
  is_dependency: bool = False
35
42
 
36
43
  def __post_init__(self):
44
+ """Ensures the errors list is initialized after the object is created."""
37
45
  if self.errors is None:
38
46
  self.errors = []
39
47
 
40
48
  @property
41
49
  def progress_percentage(self) -> float:
42
- """Calculate progress percentage"""
50
+ """Calculates the completion percentage of the job."""
43
51
  if self.total_files == 0:
44
52
  return 0.0
45
53
  return (self.processed_files / self.total_files) * 100
46
54
 
47
55
  @property
48
56
  def estimated_time_remaining(self) -> Optional[float]:
49
- """Calculate estimated time remaining"""
57
+ """Calculates the estimated time remaining based on the average time per file."""
50
58
  if self.status != JobStatus.RUNNING or self.processed_files == 0:
51
59
  return None
52
60
  elapsed = (datetime.now() - self.start_time).total_seconds()
@@ -55,13 +63,16 @@ class JobInfo:
55
63
  return remaining_files * avg_time_per_file
56
64
 
57
65
  class JobManager:
58
- """Manager for background jobs"""
66
+ """
67
+ A thread-safe manager for creating, updating, and retrieving information
68
+ about background jobs. It stores job information in memory.
69
+ """
59
70
  def __init__(self):
60
71
  self.jobs: Dict[str, JobInfo] = {}
61
- self.lock = threading.Lock()
72
+ self.lock = threading.Lock() # A lock to ensure thread-safe access to the jobs dictionary.
62
73
 
63
74
  def create_job(self, path: str, is_dependency: bool = False) -> str:
64
- """Create a new job and return job ID"""
75
+ """Creates a new job, assigns it a unique ID, and stores it."""
65
76
  job_id = str(uuid.uuid4())
66
77
  with self.lock:
67
78
  self.jobs[job_id] = JobInfo(
@@ -74,7 +85,7 @@ class JobManager:
74
85
  return job_id
75
86
 
76
87
  def update_job(self, job_id: str, **kwargs):
77
- """Update job information"""
88
+ """Updates the information for a specific job in a thread-safe manner."""
78
89
  with self.lock:
79
90
  if job_id in self.jobs:
80
91
  job = self.jobs[job_id]
@@ -83,17 +94,17 @@ class JobManager:
83
94
  setattr(job, key, value)
84
95
 
85
96
  def get_job(self, job_id: str) -> Optional[JobInfo]:
86
- """Get job information"""
97
+ """Retrieves the information for a single job."""
87
98
  with self.lock:
88
99
  return self.jobs.get(job_id)
89
100
 
90
101
  def list_jobs(self) -> List[JobInfo]:
91
- """List all jobs"""
102
+ """Returns a list of all jobs currently in the manager."""
92
103
  with self.lock:
93
104
  return list(self.jobs.values())
94
105
 
95
106
  def find_active_job_by_path(self, path: str) -> Optional[JobInfo]:
96
- """Finds the most recent, active job for a given path."""
107
+ """Finds the most recent, currently active (pending or running) job for a given path."""
97
108
  with self.lock:
98
109
  path_obj = Path(path).resolve()
99
110
 
@@ -110,7 +121,7 @@ class JobManager:
110
121
  return None
111
122
 
112
123
  def cleanup_old_jobs(self, max_age_hours: int = 24):
113
- """Clean up jobs older than specified hours"""
124
+ """Removes old, completed jobs from memory to prevent memory leaks."""
114
125
  cutoff_time = datetime.now() - timedelta(hours=max_age_hours)
115
126
  with self.lock:
116
127
  jobs_to_remove = [
@@ -118,4 +129,4 @@ class JobManager:
118
129
  if job.end_time and job.end_time < cutoff_time
119
130
  ]
120
131
  for job_id in jobs_to_remove:
121
- del self.jobs[job_id]
132
+ del self.jobs[job_id]