feat: major improvements — layout, cycles, integrations, usage examples, tests #1

Merged
dparmeev merged 15 commits from feature/improvements-v2 into main 2026-02-15 11:21:47 +03:00
24 changed files with 936 additions and 518 deletions
Showing only changes of commit c095560e13 - Show all commits

51
PR_DESCRIPTION.md Normal file
View File

@@ -0,0 +1,51 @@
# PR: Major improvements to ArchDoc
## Summary
Comprehensive refactoring and feature additions to ArchDoc — the Python architecture documentation generator. This PR improves code quality, adds new features, and significantly enhances the development experience.
**Stats:** 24 files changed, ~3900 insertions, ~1400 deletions, 50 tests
## Changes
### 🏗️ Architecture
- **Decomposed monolithic `main.rs`** into `commands/` module structure (generate, init, check, stats)
- **Added workspace `Cargo.toml`** for unified builds across both crates
- **New `cycle_detector` module** with DFS-based dependency cycle detection
### 🐍 Python Analyzer
- **Full AST traversal** — properly walks all statement types (if/for/while/try/with/match)
- **Function signatures** — extracts parameter names, types, defaults, return types
- **Method detection** — distinguishes methods from standalone functions via `self`/`cls` parameter
- **Docstring extraction** — parses first line of docstrings for symbol documentation
- **Module path computation** — correctly computes module IDs from `src_roots` config
### ✨ New Features
- **`stats` command** — project statistics with colored output and progress bar
- **Config validation** — validates project root, language, scan paths, cache age, file size formats
- **Cycle detection** — finds circular dependencies in module graph, shown in critical points section
- **`--dry-run` flag** — preview what would be generated without writing files
- **Dynamic project data** — uses config project name and current date instead of hardcoded values
- **Real usage examples** — generates Python import/call examples from analyzed symbols
- **Skip-unchanged optimization** — writer skips files that haven't changed
### 🧹 Code Quality
- **Zero `unwrap()` calls** in non-test code — proper error handling throughout
- **Zero clippy warnings** — all lints resolved
- **50 tests** — unit tests for config validation, cycle detection, caching, integration detection, error handling, and full pipeline integration tests
### 📚 Documentation
- **README.md** — badges, full command reference, configuration table, architecture overview
- **CHANGELOG.md** — complete changelog for this branch
## Testing
```bash
cargo test # 50 tests, all passing
cargo clippy # 0 warnings
cargo build # clean build
```
## Breaking Changes
None. All existing functionality preserved.

View File

@@ -105,6 +105,19 @@ pub fn generate_docs(model: &ProjectModel, out: &str, verbose: bool, _config: &C
std::fs::create_dir_all(&modules_path)?;
std::fs::create_dir_all(&files_path)?;
// Clean up stale files from previous runs
for subdir in &["modules", "files"] {
let dir = out_path.join(subdir);
if dir.exists()
&& let Ok(entries) = std::fs::read_dir(&dir) {
for entry in entries.flatten() {
if entry.path().extension().map(|e| e == "md").unwrap_or(false) {
let _ = std::fs::remove_file(entry.path());
}
}
}
}
let renderer = archdoc_core::renderer::Renderer::new();
let writer = archdoc_core::writer::DiffAwareWriter::new();

View File

@@ -51,6 +51,7 @@ pub struct FileDoc {
pub outbound_modules: Vec<String>,
pub inbound_files: Vec<String>,
pub symbols: Vec<String>,
pub file_purpose: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -142,6 +143,7 @@ pub struct ParsedModule {
pub imports: Vec<Import>,
pub symbols: Vec<Symbol>,
pub calls: Vec<Call>,
pub file_docstring: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]

View File

@@ -43,6 +43,9 @@ impl PythonAnalyzer {
let mut symbols = Vec::new();
let mut calls = Vec::new();
// Extract file-level docstring (first statement if it's a string expression)
let file_docstring = self.extract_docstring(&ast);
for stmt in &ast {
self.extract_from_statement(stmt, None, &mut imports, &mut symbols, &mut calls, 0);
}
@@ -53,6 +56,7 @@ impl PythonAnalyzer {
imports,
symbols,
calls,
file_docstring,
};
self.cache_manager.store_module(file_path, parsed_module.clone())?;
@@ -580,10 +584,25 @@ impl PythonAnalyzer {
}
}
// First pass: collect __init__.py docstrings keyed by module_id
let mut init_docstrings: std::collections::HashMap<String, String> = std::collections::HashMap::new();
for parsed_module in modules {
if parsed_module.path.file_name().map(|f| f == "__init__.py").unwrap_or(false)
&& let Some(ref ds) = parsed_module.file_docstring {
let module_id = self.compute_module_path(&parsed_module.path);
init_docstrings.insert(module_id, ds.clone());
}
}
for parsed_module in modules {
let module_id = self.compute_module_path(&parsed_module.path);
let file_id = parsed_module.path.to_string_lossy().to_string();
// Use file docstring first line as file purpose
let file_purpose = parsed_module.file_docstring.as_ref().map(|ds| {
ds.lines().next().unwrap_or(ds).to_string()
});
let file_doc = FileDoc {
id: file_id.clone(),
path: parsed_module.path.to_string_lossy().to_string(),
@@ -592,6 +611,7 @@ impl PythonAnalyzer {
outbound_modules: Vec::new(),
inbound_files: Vec::new(),
symbols: parsed_module.symbols.iter().map(|s| s.id.clone()).collect(),
file_purpose,
};
project_model.files.insert(file_id.clone(), file_doc);
@@ -601,11 +621,21 @@ impl PythonAnalyzer {
project_model.symbols.insert(symbol.id.clone(), symbol);
}
// Use __init__.py docstring for module doc_summary, or file docstring for single-file modules
let is_init = parsed_module.path.file_name().map(|f| f == "__init__.py").unwrap_or(false);
let doc_summary = if is_init {
parsed_module.file_docstring.clone()
} else {
// For non-init files, check if there's an __init__.py docstring for this module's parent
init_docstrings.get(&module_id).cloned()
.or_else(|| parsed_module.file_docstring.clone())
};
let module = Module {
id: module_id.clone(),
path: parsed_module.path.to_string_lossy().to_string(),
files: vec![file_id.clone()],
doc_summary: None,
doc_summary,
outbound_modules: Vec::new(),
inbound_modules: Vec::new(),
symbols: parsed_module.symbols.iter().map(|s| s.id.clone()).collect(),
@@ -668,6 +698,9 @@ impl PythonAnalyzer {
}
fn build_dependency_graphs(&self, project_model: &mut ProjectModel, parsed_modules: &[ParsedModule]) -> Result<(), ArchDocError> {
// Collect known internal module IDs
let known_modules: std::collections::HashSet<String> = project_model.modules.keys().cloned().collect();
for parsed_module in parsed_modules {
let from_module_id = self.compute_module_path(&parsed_module.path);
@@ -683,6 +716,41 @@ impl PythonAnalyzer {
}
}
// Populate outbound_modules and inbound_modules from edges
// Only include internal modules (ones that exist in project_model.modules)
for edge in &project_model.edges.module_import_edges {
let from_id = &edge.from_id;
// Try to match the import to an internal module
// Import "src.core.SomeClass" should match module "src.core"
let to_internal = if known_modules.contains(&edge.to_id) {
Some(edge.to_id.clone())
} else {
// Try prefix matching: "foo.bar.baz" -> check "foo.bar", "foo"
let parts: Vec<&str> = edge.to_id.split('.').collect();
let mut found = None;
for i in (1..parts.len()).rev() {
let prefix = parts[..i].join(".");
if known_modules.contains(&prefix) {
found = Some(prefix);
break;
}
}
found
};
if let Some(ref target_module) = to_internal
&& target_module != from_id {
if let Some(module) = project_model.modules.get_mut(from_id)
&& !module.outbound_modules.contains(target_module) {
module.outbound_modules.push(target_module.clone());
}
if let Some(module) = project_model.modules.get_mut(target_module)
&& !module.inbound_modules.contains(from_id) {
module.inbound_modules.push(from_id.clone());
}
}
}
for parsed_module in parsed_modules {
for call in &parsed_module.calls {
let callee_expr = call.callee_expr.clone();

View File

@@ -10,13 +10,8 @@ use chrono::Utc;
use handlebars::Handlebars;
fn sanitize_for_link(filename: &str) -> String {
filename
.chars()
.map(|c| match c {
'/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
c => c,
})
.collect()
let cleaned = filename.strip_prefix("./").unwrap_or(filename);
cleaned.replace('/', "__")
}
pub struct Renderer {
@@ -278,10 +273,68 @@ impl Renderer {
let today = Utc::now().format("%Y-%m-%d").to_string();
// Collect layout items for template
let mut layout_items = Vec::new();
for file_doc in model.files.values() {
let purpose = file_doc.file_purpose.as_deref().unwrap_or("Source file");
layout_items.push(serde_json::json!({
"path": file_doc.path,
"purpose": purpose,
"link": format!("docs/architecture/files/{}.md", sanitize_for_link(&file_doc.path))
}));
}
// Collect module items for template
let mut modules_list = Vec::new();
for (module_id, module) in &model.modules {
modules_list.push(serde_json::json!({
"name": module_id,
"symbol_count": module.symbols.len(),
"inbound_count": module.inbound_modules.len(),
"outbound_count": module.outbound_modules.len(),
"link": format!("docs/architecture/modules/{}.md", sanitize_for_link(module_id))
}));
}
// Collect critical points
let mut high_fan_in = Vec::new();
let mut high_fan_out = Vec::new();
for (symbol_id, symbol) in &model.symbols {
if symbol.metrics.fan_in > 5 {
high_fan_in.push(serde_json::json!({
"symbol": symbol_id,
"count": symbol.metrics.fan_in,
"critical": symbol.metrics.is_critical,
}));
}
if symbol.metrics.fan_out > 5 {
high_fan_out.push(serde_json::json!({
"symbol": symbol_id,
"count": symbol.metrics.fan_out,
"critical": symbol.metrics.is_critical,
}));
}
}
let cycles: Vec<_> = cycle_detector::detect_cycles(model)
.iter()
.map(|cycle| {
serde_json::json!({
"cycle_path": format!("{} → {}", cycle.join(""), cycle.first().unwrap_or(&String::new()))
})
})
.collect();
// Project statistics
let project_description = format!(
"Python project with {} modules, {} files, and {} symbols.",
model.modules.len(), model.files.len(), model.symbols.len()
);
// Prepare data for template
let data = serde_json::json!({
"project_name": project_name,
"project_description": "<FILL_MANUALLY: what this project does in 37 lines>",
"project_description": project_description,
"created_date": &today,
"updated_date": &today,
"key_decisions": ["<FILL_MANUALLY>"],
@@ -290,6 +343,12 @@ impl Renderer {
"db_integrations": db_integrations,
"http_integrations": http_integrations,
"queue_integrations": queue_integrations,
"rails_summary": "\n\nNo tooling information available.\n",
"layout_items": layout_items,
"modules": modules_list,
"high_fan_in": high_fan_in,
"high_fan_out": high_fan_out,
"cycles": cycles,
});
self.templates.render("architecture_md", &data)
@@ -464,9 +523,10 @@ impl Renderer {
let mut layout_items = Vec::new();
for file_doc in model.files.values() {
let purpose = file_doc.file_purpose.as_deref().unwrap_or("Source file");
layout_items.push(serde_json::json!({
"path": file_doc.path,
"purpose": "Source file",
"purpose": purpose,
"link": format!("docs/architecture/files/{}.md", sanitize_for_link(&file_doc.path))
}));
}
@@ -603,9 +663,10 @@ impl Renderer {
let mut layout_items = Vec::new();
for file_doc in model.files.values() {
let purpose = file_doc.file_purpose.as_deref().unwrap_or("Source file");
layout_items.push(serde_json::json!({
"path": file_doc.path,
"purpose": "Source file",
"purpose": purpose,
"link": format!("files/{}.md", sanitize_for_link(&file_doc.path))
}));
}

View File

@@ -16,7 +16,7 @@
## Document metadata
- **Created:** 2026-01-25
- **Updated:** 2026-01-25
- **Updated:** 2026-02-15
- **Generated by:** archdoc (cli) v0.1
---
@@ -34,9 +34,9 @@ No tooling information available.
| Path | Purpose | Link |
|------|---------|------|
| ./src/__init__.py | Source file | [details](docs/architecture/files/._src___init__.py.md) |
| ./src/utils.py | Source file | [details](docs/architecture/files/._src_utils.py.md) |
| ./src/core.py | Source file | [details](docs/architecture/files/._src_core.py.md) |
| ./src/__init__.py | Test project package. | [details](docs/architecture/files/src____init__.py.md) |
| ./src/utils.py | Utility functions for the test project. | [details](docs/architecture/files/src__utils.py.md) |
| ./src/core.py | Core module with database and HTTP integrations. | [details](docs/architecture/files/src__core.py.md) |
<!-- ARCHDOC:END section=layout -->
---
@@ -46,9 +46,9 @@ No tooling information available.
| Module | Symbols | Inbound | Outbound | Link |
|--------|---------|---------|----------|------|
| ./src/__init__.py | 0 | 0 | 0 | [details](docs/architecture/modules/._src___init__.py.md) |
| ./src/utils.py | 4 | 0 | 0 | [details](docs/architecture/modules/._src_utils.py.md) |
| ./src/core.py | 6 | 0 | 0 | [details](docs/architecture/modules/._src_core.py.md) |
| core | 6 | 0 | 0 | [details](docs/architecture/modules/core.md) |
| utils | 4 | 0 | 0 | [details](docs/architecture/modules/utils.md) |
| src | 0 | 0 | 0 | [details](docs/architecture/modules/src.md) |
<!-- ARCHDOC:END section=modules_index -->
---

View File

@@ -1,3 +0,0 @@
# File: ../test-project/src/__init__.py
TODO: Add file documentation

View File

@@ -1,3 +0,0 @@
# File: ../test-project/src/core.py
TODO: Add file documentation

View File

@@ -1,3 +0,0 @@
# File: ../test-project/src/utils.py
TODO: Add file documentation

View File

@@ -1,36 +0,0 @@
# File: ./src/core.py
- **Module:** ./src/core.py
- **Defined symbols:** 6
- **Imports:** 2
<!-- MANUAL:BEGIN -->
## File intent (manual)
<FILL_MANUALLY>
<!-- MANUAL:END -->
---
## Imports & file-level dependencies
<!-- ARCHDOC:BEGIN section=file_imports -->
> Generated. Do not edit inside this block.
- sqlite3
- requests
<!-- ARCHDOC:END section=file_imports -->
---
## Symbols index
<!-- ARCHDOC:BEGIN section=symbols_index -->
> Generated. Do not edit inside this block.
- [DatabaseManager](._src_core.py#DatabaseManager)
- [__init__](._src_core.py#__init__)
- [connect](._src_core.py#connect)
- [execute_query](._src_core.py#execute_query)
- [fetch_external_data](._src_core.py#fetch_external_data)
- [process_user_data](._src_core.py#process_user_data)
<!-- ARCHDOC:END section=symbols_index -->
---
## Symbol details

View File

@@ -1,34 +0,0 @@
# File: ./src/utils.py
- **Module:** ./src/utils.py
- **Defined symbols:** 4
- **Imports:** 2
<!-- MANUAL:BEGIN -->
## File intent (manual)
<FILL_MANUALLY>
<!-- MANUAL:END -->
---
## Imports & file-level dependencies
<!-- ARCHDOC:BEGIN section=file_imports -->
> Generated. Do not edit inside this block.
- json
- os
<!-- ARCHDOC:END section=file_imports -->
---
## Symbols index
<!-- ARCHDOC:BEGIN section=symbols_index -->
> Generated. Do not edit inside this block.
- [load_config](._src_utils.py#load_config)
- [save_config](._src_utils.py#save_config)
- [get_file_size](._src_utils.py#get_file_size)
- [format_bytes](._src_utils.py#format_bytes)
<!-- ARCHDOC:END section=symbols_index -->
---
## Symbol details

View File

@@ -1,6 +1,6 @@
# File: ./src/__init__.py
- **Module:** ./src/__init__.py
- **Module:** src
- **Defined symbols:** 0
- **Imports:** 0

View File

@@ -0,0 +1,276 @@
# File: ./src/core.py
- **Module:** core
- **Defined symbols:** 6
- **Imports:** 2
<!-- MANUAL:BEGIN -->
## File intent (manual)
<FILL_MANUALLY>
<!-- MANUAL:END -->
---
## Imports & file-level dependencies
<!-- ARCHDOC:BEGIN section=file_imports -->
> Generated. Do not edit inside this block.
- sqlite3
- requests
<!-- ARCHDOC:END section=file_imports -->
---
## Symbols index
<!-- ARCHDOC:BEGIN section=symbols_index -->
> Generated. Do not edit inside this block.
- `DatabaseManager` (Class)
- `DatabaseManager.__init__` (Method)
- `DatabaseManager.connect` (Method)
- `DatabaseManager.execute_query` (Method)
- `fetch_external_data` (Function)
- `process_user_data` (Function)
<!-- ARCHDOC:END section=symbols_index -->
---
## Symbol details
<!-- ARCHDOC:BEGIN symbol id=DatabaseManager --><a id="DatabaseManager"></a>
### `DatabaseManager`
- **Kind:** Class
- **Signature:** `class DatabaseManager`
- **Docstring:** `Manages database connections and operations.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: yes
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 2
- fan-out: 4
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=DatabaseManager -->
<!-- ARCHDOC:BEGIN symbol id=DatabaseManager.__init__ --><a id="DatabaseManager.__init__"></a>
### `DatabaseManager.__init__`
- **Kind:** Method
- **Signature:** `def __init__(self, db_path: str)`
- **Docstring:** `No documentation available`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 0
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=DatabaseManager.__init__ -->
<!-- ARCHDOC:BEGIN symbol id=DatabaseManager.connect --><a id="DatabaseManager.connect"></a>
### `DatabaseManager.connect`
- **Kind:** Method
- **Signature:** `def connect(self)`
- **Docstring:** `Connect to the database.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: yes
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 1
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=DatabaseManager.connect -->
<!-- ARCHDOC:BEGIN symbol id=DatabaseManager.execute_query --><a id="DatabaseManager.execute_query"></a>
### `DatabaseManager.execute_query`
- **Kind:** Method
- **Signature:** `def execute_query(self, query: str)`
- **Docstring:** `Execute a database query.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 3
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=DatabaseManager.execute_query -->
<!-- ARCHDOC:BEGIN symbol id=fetch_external_data --><a id="fetch_external_data"></a>
### `fetch_external_data`
- **Kind:** Function
- **Signature:** `def fetch_external_data(url: str)`
- **Docstring:** `Fetch data from an external API.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: yes
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 2
- fan-out: 2
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=fetch_external_data -->
<!-- ARCHDOC:BEGIN symbol id=process_user_data --><a id="process_user_data"></a>
### `process_user_data`
- **Kind:** Function
- **Signature:** `def process_user_data(user_id: int)`
- **Docstring:** `Process user data with database and external API calls.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 4
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=process_user_data -->

View File

@@ -0,0 +1,194 @@
# File: ./src/utils.py
- **Module:** utils
- **Defined symbols:** 4
- **Imports:** 2
<!-- MANUAL:BEGIN -->
## File intent (manual)
<FILL_MANUALLY>
<!-- MANUAL:END -->
---
## Imports & file-level dependencies
<!-- ARCHDOC:BEGIN section=file_imports -->
> Generated. Do not edit inside this block.
- json
- os
<!-- ARCHDOC:END section=file_imports -->
---
## Symbols index
<!-- ARCHDOC:BEGIN section=symbols_index -->
> Generated. Do not edit inside this block.
- `load_config` (Function)
- `save_config` (Function)
- `get_file_size` (Function)
- `format_bytes` (Function)
<!-- ARCHDOC:END section=symbols_index -->
---
## Symbol details
<!-- ARCHDOC:BEGIN symbol id=load_config --><a id="load_config"></a>
### `load_config`
- **Kind:** Function
- **Signature:** `def load_config(config_path: str)`
- **Docstring:** `Load configuration from a JSON file.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 2
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=load_config -->
<!-- ARCHDOC:BEGIN symbol id=save_config --><a id="save_config"></a>
### `save_config`
- **Kind:** Function
- **Signature:** `def save_config(config: dict, config_path: str)`
- **Docstring:** `Save configuration to a JSON file.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 2
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=save_config -->
<!-- ARCHDOC:BEGIN symbol id=get_file_size --><a id="get_file_size"></a>
### `get_file_size`
- **Kind:** Function
- **Signature:** `def get_file_size(filepath: str)`
- **Docstring:** `Get the size of a file in bytes.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 1
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=get_file_size -->
<!-- ARCHDOC:BEGIN symbol id=format_bytes --><a id="format_bytes"></a>
### `format_bytes`
- **Kind:** Function
- **Signature:** `def format_bytes(size: int)`
- **Docstring:** `Format bytes into a human-readable string.`
#### What it does
<!-- ARCHDOC:BEGIN section=purpose -->
extracted from AST
<!-- ARCHDOC:END section=purpose -->
#### Relations
<!-- ARCHDOC:BEGIN section=relations -->
**Outbound calls (best-effort):**
**Inbound (used by) (best-effort):**
<!-- ARCHDOC:END section=relations -->
#### Integrations (heuristic)
<!-- ARCHDOC:BEGIN section=integrations -->
- HTTP: no
- DB: no
- Queue/Tasks: no
<!-- ARCHDOC:END section=integrations -->
#### Risk / impact
<!-- ARCHDOC:BEGIN section=impact -->
- fan-in: 0
- fan-out: 0
- cycle participant: no
- critical: no
<!-- ARCHDOC:END section=impact -->
<!-- MANUAL:BEGIN -->
#### Manual notes
<FILL_MANUALLY>
<!-- MANUAL:END -->
<!-- ARCHDOC:END symbol id=format_bytes -->

View File

@@ -0,0 +1,18 @@
# Repository layout
<!-- MANUAL:BEGIN -->
## Manual overrides
- `src/app/` — <FILL_MANUALLY>
<!-- MANUAL:END -->
---
## Detected structure
<!-- ARCHDOC:BEGIN section=layout_detected -->
> Generated. Do not edit inside this block.
| Path | Purpose | Link |
|------|---------|------|
| ./src/__init__.py | Test project package. | [details](files/src____init__.py.md) |
| ./src/utils.py | Utility functions for the test project. | [details](files/src__utils.py.md) |
| ./src/core.py | Core module with database and HTTP integrations. | [details](files/src__core.py.md) |
<!-- ARCHDOC:END section=layout_detected -->

View File

@@ -1,27 +0,0 @@
# Module: ../test-project/src/__init__.py
No summary available
## Symbols
## Dependencies
### Imports
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -1,106 +0,0 @@
# Module: ../test-project/src/core.py
No summary available
## Symbols
### DatabaseManager
class DatabaseManager
No documentation available
**Type:** Class
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### __init__
def __init__(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### connect
def connect(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### execute_query
def execute_query(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### fetch_external_data
def fetch_external_data(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### process_user_data
def process_user_data(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 1
## Dependencies
### Imports
- sqlite3
- requests
### Outbound Modules
### Inbound Modules
## Integrations
### Database Integrations
- DatabaseManager
- connect
### HTTP/API Integrations
- fetch_external_data
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -1,77 +0,0 @@
# Module: ../test-project/src/utils.py
No summary available
## Symbols
### load_config
def load_config(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### save_config
def save_config(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### get_file_size
def get_file_size(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### format_bytes
def format_bytes(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
## Dependencies
### Imports
- json
- os
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -1,27 +0,0 @@
# Module: ./src/__init__.py
No summary available
## Symbols
## Dependencies
### Imports
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -1,106 +0,0 @@
# Module: ./src/core.py
No summary available
## Symbols
### DatabaseManager
class DatabaseManager
No documentation available
**Type:** Class
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### __init__
def __init__(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### connect
def connect(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### execute_query
def execute_query(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### fetch_external_data
def fetch_external_data(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### process_user_data
def process_user_data(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 1
## Dependencies
### Imports
- sqlite3
- requests
### Outbound Modules
### Inbound Modules
## Integrations
### Database Integrations
- DatabaseManager
- connect
### HTTP/API Integrations
- fetch_external_data
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -1,77 +0,0 @@
# Module: ./src/utils.py
No summary available
## Symbols
### load_config
def load_config(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### save_config
def save_config(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### get_file_size
def get_file_size(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### format_bytes
def format_bytes(...)
No documentation available
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
## Dependencies
### Imports
- json
- os
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
// Example usage of module functions
// TODO: Add real usage examples based on module analysis
```

View File

@@ -0,0 +1,116 @@
# Module: core
Core module with database and HTTP integrations.
## Symbols
### DatabaseManager
class DatabaseManager
Manages database connections and operations.
**Type:** Class
**Metrics:**
- Fan-in: 2
- Fan-out: 4
### DatabaseManager.__init__
def __init__(self, db_path: str)
No documentation available
**Type:** Method
**Metrics:**
- Fan-in: 0
- Fan-out: 0
### DatabaseManager.connect
def connect(self)
Connect to the database.
**Type:** Method
**Metrics:**
- Fan-in: 0
- Fan-out: 1
### DatabaseManager.execute_query
def execute_query(self, query: str)
Execute a database query.
**Type:** Method
**Metrics:**
- Fan-in: 0
- Fan-out: 3
### fetch_external_data
def fetch_external_data(url: str)
Fetch data from an external API.
**Type:** Function
**Metrics:**
- Fan-in: 2
- Fan-out: 2
### process_user_data
def process_user_data(user_id: int)
Process user data with database and external API calls.
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 4
## Dependencies
### Imports
- sqlite3
- requests
### Outbound Modules
### Inbound Modules
## Integrations
### Database Integrations
- DatabaseManager
- DatabaseManager.connect
### HTTP/API Integrations
- fetch_external_data
## Usage Examples
```python
from core import DatabaseManager
instance = DatabaseManager()
```
```python
from core import fetch_external_data
result = fetch_external_data(url)
```
```python
from core import process_user_data
result = process_user_data(user_id)
```

View File

@@ -0,0 +1,26 @@
# Module: src
Test project package.
## Symbols
## Dependencies
### Imports
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
import src
```

View File

@@ -0,0 +1,92 @@
# Module: utils
Utility functions for the test project.
## Symbols
### load_config
def load_config(config_path: str)
Load configuration from a JSON file.
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 2
### save_config
def save_config(config: dict, config_path: str)
Save configuration to a JSON file.
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 2
### get_file_size
def get_file_size(filepath: str)
Get the size of a file in bytes.
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 1
### format_bytes
def format_bytes(size: int)
Format bytes into a human-readable string.
**Type:** Function
**Metrics:**
- Fan-in: 0
- Fan-out: 0
## Dependencies
### Imports
- json
- os
### Outbound Modules
### Inbound Modules
## Integrations
## Usage Examples
```python
from utils import load_config
result = load_config(config_path)
```
```python
from utils import save_config
result = save_config(config, config_path)
```
```python
from utils import get_file_size
result = get_file_size(filepath)
```
```python
from utils import format_bytes
result = format_bytes(size)
```