File¶
file
¶
Filesystem helpers for path manipulation, file I/O, symlink management, and addon discovery.
Sections
- Path utilities: path predicates, canonical path computation, prefix checks
- File I/O: plain-text file reading/writing and directory copy
- Symlinks: listing, mapping, rewriting, and materialising symlinks
- Addons: locating and collecting Odoo addon directories
Functions:
| Name | Description |
|---|---|
build_compose |
Render a docker-compose.yml file from the project template. |
check_prefix |
Check whether a path is equal to or descends from a prefix directory. |
collect_addon_paths |
Collect (addon_path, unported) pairs from an addons directory. |
copytree |
Copy a directory tree from src to dst, preserving symlinks. |
create_symlink |
Create a symlink at the repo root pointing to an addon directory. |
desired_path |
Build the desired local path for a git repository URL. |
ensure_parent |
Ensure the parent directory of a path exists, creating it if needed. |
file_updater |
Update a file with new content, either replacing the entire file or a section between tags. |
find_addon_dirs |
Return all addon directories found under a root path. |
find_addons |
Yield AddonInfo for every Odoo addon found under a root directory. |
find_modified_addons |
Return the names of addons containing any of the given file paths. |
get_addons_diff |
Classify addon changes between base_ref and HEAD into new, updated, and removed. |
get_excluded_addon_names |
Return addon names that should be excluded from pre-commit checks. |
get_filtered_addon_names |
Return names of owned, installable, non-symlinked addons. |
get_odoo_sources_dirs |
Resolve the community and enterprise source directories for a given Odoo version. |
get_requirements_diff |
Compare the current requirements file against Python deps declared in addon manifests. |
get_symlink_complete_map |
Return a mapping of symlink parent dirs to all their target names. |
get_symlink_map |
Build a mapping of symlink parent directories to their single target name. |
is_dir_empty |
Check whether a directory exists and contains no entries. |
is_pull_request_path |
Detect whether a submodule path looks like a pull request path. |
list_symlinks |
Collect symlink targets found recursively under a directory. |
make_migration_command |
Build the content of a migration shell script from addon change lists. |
materialize_symlink |
Replace a symlink pointing to a directory with a physical copy of its target. |
parse_odoo_version |
Read and parse the Odoo version file into structured image information. |
parse_packages |
Read and return the sorted list of packages from the project packages file. |
parse_requirements |
Read and return the sorted list of entries from the project requirements file. |
parse_text_file |
Parse a text file's content into a list of non-empty, stripped lines. |
read_and_parse |
Read a text file and return its non-empty, sorted lines. |
read_tagged_block |
Return the raw content between start_tag and end_tag in a file. |
relpath |
Compute a relative path from one location to another. |
rewrite_symlink |
Rewrite a symlink's target by replacing a path prefix. |
volume_prefix |
Derive a Docker-safe volume prefix from the repo directory name. |
write_migration_script |
Write a migration script to the configured file path and mark it executable. |
write_text_file |
Write a list of lines to a text file. |
build_compose
¶
build_compose(odoo_version: float, image: str, port: int, prefix: str, dev: bool, with_maildev: bool, with_sftp: bool) -> str
Render a docker-compose.yml file from the project template.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
float
|
Numeric Odoo major version (e.g. |
required |
|
str
|
Full Docker image reference for the Odoo service (e.g. |
required |
|
int
|
Host port to map to Odoo's internal port 8069. |
required |
|
str
|
Docker-safe volume name prefix, typically derived from the repo name. |
required |
|
bool
|
Whether to append |
required |
|
bool
|
Include the maildev SMTP catch-all service. |
required |
|
bool
|
Include the SFTP service. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Rendered docker-compose.yml content as a string, ready to write to disk. |
Source code in src/oops/io/file.py
check_prefix
¶
Check whether a path is equal to or descends from a prefix directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Path to test. |
required |
|
str
|
Ancestor path to check against. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if path equals prefix or is nested inside it, False otherwise. |
Source code in src/oops/io/file.py
collect_addon_paths
¶
collect_addon_paths(addons_dir: Path) -> list
Collect (addon_path, unported) pairs from an addons directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Root addons directory to inspect. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Sorted list of (Path, bool) pairs where the bool indicates |
list
|
whether the addon lives under the unported subdirectory. |
Source code in src/oops/io/file.py
copytree
¶
Copy a directory tree from src to dst, preserving symlinks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Source directory to copy. |
required |
|
Path
|
Destination path, must not already exist. |
required |
|
bool
|
If True, skip .git directories. Defaults to True. |
True
|
Source code in src/oops/io/file.py
create_symlink
¶
Create a symlink at the repo root pointing to an addon directory.
Skips creation if a file or symlink with the same name already exists at the repo root, printing a warning in that case.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Path to the addon directory to link. |
required |
|
Path
|
Repository root where the symlink will be created. |
required |
Returns:
| Type | Description |
|---|---|
Optional[str]
|
The symlink name (stem of addon_dir) if created, or None if skipped. |
Source code in src/oops/io/file.py
desired_path
¶
desired_path(url: str, pull_request: bool = False, prefix: Optional[str] = None, suffix: Optional[str] = None) -> str
Build the desired local path for a git repository URL.
Produces <prefix>/<owner>/<repo>/<suffix>, inserting a pull-request
segment after the prefix when pull_request is True.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
GitHub repository URL (HTTPS or SSH). |
required |
|
bool
|
If True, insert the pull-request directory segment. Defaults to False. |
False
|
|
Optional[str]
|
Optional path prefix prepended before the owner segment. |
None
|
|
Optional[str]
|
Optional path segment appended after the repo name. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Relative filesystem path derived from the repository URL components. |
Source code in src/oops/io/file.py
ensure_parent
¶
file_updater
¶
file_updater(filepath: str, new_inner_content: str, start_tag: Optional[str] = None, end_tag: Optional[str] = None, padding: str = '\n', append_position: str | bool = 'bottom', dry_run: bool = False) -> bool
Update a file with new content, either replacing the entire file or a section between tags.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Path to the file to update. |
required |
|
str
|
New content to insert. |
required |
|
Optional[str]
|
Start tag for targeted replacement (optional). |
None
|
|
Optional[str]
|
End tag for targeted replacement (optional). |
None
|
|
str
|
Padding to add around the new content (default: newline). |
'\n'
|
|
str | bool
|
Where to insert the tagged block when tags are absent from the file.
|
'bottom'
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the file was updated, False if no changes have been made. |
Source code in src/oops/io/file.py
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | |
find_addon_dirs
¶
Return all addon directories found under a root path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Directory to search recursively. |
required |
|
bool
|
If True, descend into pull-request subdirectories. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
list
|
List of Path objects for each directory containing a manifest file. |
Source code in src/oops/io/file.py
find_addons
¶
Yield AddonInfo for every Odoo addon found under a root directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Directory to search recursively (symlinked first-level dirs are followed). |
required |
|
bool
|
If True, do not recurse deeper than one level into subdirectories. Defaults to False. |
False
|
Yields:
| Type | Description |
|---|---|
AddonInfo
|
AddonInfo for each addon directory containing a manifest file. |
Source code in src/oops/io/file.py
find_modified_addons
¶
Return the names of addons containing any of the given file paths.
Walks up each file path until a directory with an Odoo manifest is found.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list
|
List of file paths to inspect. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Sorted list of addon directory names that contain at least one of the files. |
Source code in src/oops/io/file.py
get_addons_diff
¶
Classify addon changes between base_ref and HEAD into new, updated, and removed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Repo
|
GitPython Repo object for the local repository. |
required |
|
str
|
Git ref (tag, branch, or commit-ish) to compare against HEAD. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Tuple of (new_addons, updated_addons, removed_addons), each a sorted list |
list
|
of addon names. |
Source code in src/oops/io/file.py
get_excluded_addon_names
¶
Return addon names that should be excluded from pre-commit checks.
An addon is excluded when it is not installable or its author does not
match config.manifest.author (i.e. it is a third-party addon).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Root directory of the local repository. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Sorted list of technical addon names to exclude. |
Source code in src/oops/io/file.py
get_filtered_addon_names
¶
Return names of owned, installable, non-symlinked addons.
Selects addons that are directly in the repository (not symlinks to
third-party modules), are installable, and are authored by
config.manifest.author. Intended as the default scope for manifest
lint and fix commands.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Root directory of the local repository. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Sorted list of technical addon names matching the criteria. |
Source code in src/oops/io/file.py
get_odoo_sources_dirs
¶
Resolve the community and enterprise source directories for a given Odoo version.
The base directory is taken from base_dir when provided, otherwise falls back
to odoo.sources_dir in ~/.oops.yaml. The version sub-directory is created
if it does not exist yet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Odoo version string used as the sub-directory name (e.g. |
required |
|
Optional[Path]
|
Optional explicit root for Odoo sources. Overrides the config value. |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
A |
Path
|
pointing to the |
tuple[Path, Path]
|
|
tuple[Path, Path]
|
exist on disk — the caller is responsible for checking existence. |
Raises:
| Type | Description |
|---|---|
UsageError
|
When neither |
Source code in src/oops/io/file.py
get_requirements_diff
¶
Compare the current requirements file against Python deps declared in addon manifests.
Collects all python entries from external_dependencies across every
addon found in repo_path, sorts them, and runs a line-level diff against
the existing requirement_file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Root of the repository to scan for addons. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
A three-element tuple |
list
|
|
list
|
|
tuple[bool, list, list]
|
|
Source code in src/oops/io/file.py
get_symlink_complete_map
¶
Return a mapping of symlink parent dirs to all their target names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Root directory to scan for symlinks. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Dict mapping each parent directory path to a list of target names |
dict
|
found under it. |
Source code in src/oops/io/file.py
get_symlink_map
¶
Build a mapping of symlink parent directories to their single target name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Root directory to scan for symlinks. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Dict mapping each parent directory path to one target name. |
dict
|
Assumes at most one symlink per parent directory. |
Source code in src/oops/io/file.py
is_dir_empty
¶
is_pull_request_path
¶
Detect whether a submodule path looks like a pull request path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Optional[str]
|
Submodule path string to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the path matches pull-request naming conventions, False otherwise. |
Source code in src/oops/io/file.py
list_symlinks
¶
Collect symlink targets found recursively under a directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
PathLike
|
Root directory to walk. |
required |
|
bool
|
If True, only return targets of broken symlinks. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of symlink target strings found under path. |
Source code in src/oops/io/file.py
make_migration_command
¶
make_migration_command(new_addons: Optional[list] = None, updated_addons: Optional[list] = None, removed_addons: Optional[list] = None, release: Optional[str] = None) -> str
Build the content of a migration shell script from addon change lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Optional[list]
|
Addons to install with |
None
|
|
Optional[list]
|
Addons to update with |
None
|
|
Optional[list]
|
Addons that were removed; included as a comment only. |
None
|
|
Optional[str]
|
Release label used in the script header. Defaults to "Unreleased". |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Full migration script content as a string, including the shebang line. |
Source code in src/oops/io/file.py
materialize_symlink
¶
materialize_symlink(symlink_path: Path, dry_run: bool) -> None
Replace a symlink pointing to a directory with a physical copy of its target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Path to the symlink to materialize. |
required |
|
bool
|
If True, validate inputs but make no filesystem changes. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the path does not exist, is not a symlink, its target is not a directory, or materialization fails. |
Source code in src/oops/io/file.py
parse_odoo_version
¶
Read and parse the Odoo version file into structured image information.
Reads the first non-empty line of the version file and parses it as a Docker image tag, extracting the major version, edition, registry, release date, and flags.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Project root directory containing the Odoo version file. |
required |
Returns:
| Type | Description |
|---|---|
ImageInfo
|
ImageInfo populated with registry, major version, edition, release date, and flags. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the version file is empty, missing, or the tag format is unrecognised. |
Source code in src/oops/io/file.py
parse_packages
¶
parse_requirements
¶
Read and return the sorted list of entries from the project requirements file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Project root directory containing the requirements file. |
required |
Returns:
| Type | Description |
|---|---|
list
|
Sorted list of requirement strings, or an empty list if the file does not exist. |
Source code in src/oops/io/file.py
parse_text_file
¶
Parse a text file's content into a list of non-empty, stripped lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Raw file content as a string. |
required |
|
bool
|
Whether to deduplicate the result. Defaults to True. |
True
|
Returns:
| Type | Description |
|---|---|
list
|
List of cleaned, non-empty lines. |
Source code in src/oops/io/file.py
read_and_parse
¶
Read a text file and return its non-empty, sorted lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Path to the text file to read. |
required |
|
bool
|
Whether to deduplicate the result. Defaults to True. |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted list of cleaned, non-empty lines from the file. |
Source code in src/oops/io/file.py
read_tagged_block
¶
Return the raw content between start_tag and end_tag in a file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Union[str, Path]
|
Path to the file to read. |
required |
|
str
|
Exact string marking the beginning of the block. |
required |
|
str
|
Exact string marking the end of the block. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The text between the two tags, or an empty string when the file does |
str
|
not exist or the tags are not found. |
Source code in src/oops/io/file.py
relpath
¶
Compute a relative path from one location to another.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
The starting directory. |
required |
|
Path
|
The target path to reach. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Relative path string from from_path to to_path. |
Source code in src/oops/io/file.py
rewrite_symlink
¶
rewrite_symlink(link: Path, old_prefix: str, new_prefix: str) -> bool
Rewrite a symlink's target by replacing a path prefix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Path to the symlink to rewrite. |
required |
|
str
|
Prefix to replace in the symlink target. |
required |
|
str
|
Replacement prefix. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the symlink was rewritten, False if the target did not match. |
Source code in src/oops/io/file.py
volume_prefix
¶
Derive a Docker-safe volume prefix from the repo directory name.
Strips a leading odoo- prefix (common convention) then
replaces any non-alphanumeric character with an underscore.
Examples:
odoo-my-project → my_project
my-project → my_project
odoo-client_v2 → client_v2
Source code in src/oops/io/file.py
write_migration_script
¶
Write a migration script to the configured file path and mark it executable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Full script content to write. |
required |
|
bool
|
If True, print to stdout instead of writing to disk. Defaults to False. |
False
|
Source code in src/oops/io/file.py
write_text_file
¶
Write a list of lines to a text file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Destination file path. |
required |
|
list
|
Lines to write, joined by new_line. |
required |
|
str
|
Line separator. Defaults to "\n". |
'\n'
|
|
bool
|
If True, append a trailing newline. Defaults to True. |
True
|