Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8b85cf4
fix: enhance command descriptions and options in run and tui commands
gitmobkab May 27, 2026
b3a5c13
refactor: refactored toml loader from tomllib to tomlkit
gitmobkab May 27, 2026
9633ae6
fix: update profile loading logic and add console output control
gitmobkab May 27, 2026
a9bf083
fix: fixed bug in json formatter
gitmobkab May 27, 2026
c94041d
refactor: consolidate formatter types and improve SQL formatting logic
gitmobkab May 27, 2026
833fa5c
fix: update return type of enum function to Any
gitmobkab May 27, 2026
a481c89
refactor: update generator type annotations and updated the generator…
gitmobkab May 27, 2026
afd518c
fix: handle all exceptions in row generation instead of specific Gene…
gitmobkab May 27, 2026
994dd30
refactor: update fetch handling and improve error messages in runners.py
gitmobkab May 27, 2026
7f0d6b9
refactor: skip adapter tests in non-prod environments and clean up te…
gitmobkab May 27, 2026
7d177aa
refactor: update exception handling in TOML parsing tests and clean u…
gitmobkab May 27, 2026
554fd84
test: add unit tests for row generation and fetch input parsing
gitmobkab May 27, 2026
d172f86
test: add CLI tests for skip-config and dumps functionality
gitmobkab May 27, 2026
bba99bf
test: add initial tests and fixtures for formatter functionality
gitmobkab May 27, 2026
b8d140c
test: add parameterized tests for generator default arguments
gitmobkab May 27, 2026
aa988cc
docs: improve CLI usage documentation and fix minor typos
gitmobkab May 27, 2026
e017ce7
chore: downgrade Python version to 3.10 and update dependencies accor…
gitmobkab May 27, 2026
73f8192
Refactor code structure for improved readability and maintainability
gitmobkab May 27, 2026
e312e28
chore: update version to 0.3.2 and document changes in CHANGELOG
gitmobkab May 27, 2026
0ca311f
chore: add ENV variable to CI environment configuration
gitmobkab May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
--health-retries=3

env:
ENV: prod
COPIA_TEST_MYSQL_HOST: 127.0.0.1
COPIA_TEST_MYSQL_PORT: 3306
COPIA_TEST_MYSQL_USER: root
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11
3.10
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.3.2] - 2026-05-27

### Fixed
- Fixed a bug where the generator `uuid` would crash in the `copia run --dumps json` command and other generators that returned non-basic types (like `datetime` objects)


### Changed

- Now passing the `--dumps` flag will directly dump the generated values and disable non-error logs.
- Improved the SQL formatter to be more accurate to the actual SQL syntax.


## [0.3.1] - 2026-05-18

### Fixed
Expand Down
55 changes: 53 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Done.
pip install copia-seed
```

Requires Python 3.13+. MySQL and MariaDB included. PostgreSQL available as an optional dependency.
Requires Python 3.10+. MySQL and MariaDB included. PostgreSQL available as an optional dependency.

## Quickstart

Expand All @@ -106,9 +106,60 @@ copia run --dumps json users.copia --skip-config
echo "id:uuid() name:username()" | copia run --dumps json --skip-config
```

## Usage

For detailed usage instructions, see the [CLI documentation](cli.md).

copia's main entry point is the `copia` command, which has several subcommands for different tasks:
- `copia tui` : launch the interactive TUI (requires a valid config profile)
- `copia run` : parse and run a file content, with options for dumping output, skipping confirmation, and more
- `copia list` : list all available profiles in the config files
- `copia init` : create a new config file with a default profile

## Configuration

Copia config files are TOML files that define what we call "profiles".

Those profiles are just named sets of configuration values, that tell copia how to connect to the database.

a config file can have multiples profiles and usually looks like this:

```toml
[profiles.default]
adapter = "mysql"
host = "localhost"
port = 3306
database = "mydb"
user = "root"

[profiles.staging]
adapter = "postgres"
host = "staging-db.example.com"
port = 5432
database = "stagingdb"
user = "admin"
password = "Y@urRe@lly$trongP@ssw0rd"
```

any command that takes a profile name as an argument or flag will assume the "default" profile if the name is not provided, so in the example above, both `copia tui` and `copia tui staging` will work without issues.

### Scopes

copia supports two scopes for configuration profiles: `global` and `local`.

the global config file is usually located at `{CONFIG_DIR}/copia/profiles.toml` and is meant to store profiles that are shared across all your projects.

while the local config file is located at `.copia.toml` in your project directory and is meant to store profiles that are specific to that project.

In some situations, you might want to limit the search for profiles to a specific config file.

For example you might have a profile named "staging" in both your global and local config files, but with different connection details, and you want to make sure you're using the one in the global config file.



## Documentation

→ **[gitmobkab.github.io/copia](https://gitmobkab.github.io/copia/)**
→ **[For more details on copia see the documentation](https://gitmobkab.github.io/copia/)**

---

Expand Down
33 changes: 24 additions & 9 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ Supported shells: Bash, Zsh, Fish, PowerShell.

Generate a template config file.

#### **Usage:** `copia init [OPTIONS]`
#### **Usage**

**Usage:** `copia init [OPTIONS]`

By default, creates a local `.copia.toml` in the current directory.

Expand All @@ -84,7 +86,9 @@ copia init --global

List all profiles defined across both global and local config files.

#### **Usage:** `copia list [OPTIONS]`
#### **Usage**

**Usage:** `copia list [OPTIONS]`

#### **Options**

Expand Down Expand Up @@ -136,7 +140,9 @@ copia list

Launch the interactive tui.

#### **Usage:** `copia tui [OPTIONS] [PROFILE_NAME]`
#### **Usage**

**Usage:** `copia tui [OPTIONS] [PROFILE_NAME]`

#### **Arguments**

Expand All @@ -160,20 +166,22 @@ copia tui

copia tui staging

copia tui -g # (1)!
copia tui --global # (1)!

copia tui -g "mark-and-deceive" # (2)!
copia tui --global "mark-and-deceive" # (2)!
```

1. look for the profile named "default" **only** in the global config file
1. look for the profile named "default" **only** in the global config file, skip the local config entirely
2. look for the profile named "mark-and-deceive" **only** in the local config, no fallback to the global config


### `copia run`

Parse and run a file content

#### **Usage:** `copia run [OPTIONS] [FILE]`
#### **Usage**

**Usage:** `copia run [OPTIONS] [FILE]`

#### **Arguments**

Expand Down Expand Up @@ -207,7 +215,7 @@ Parse and run a file content
```bash title="users.copia"
id: uuid()
name: name()
age: int(0, 50)
age: ranged_int(0, 50)
```

```bash
Expand Down Expand Up @@ -269,4 +277,11 @@ Copia uses structured exit codes to make scripting and error handling predictabl
| `7` | `GENERATION_ERROR` | An error occurred while generating values. |
| `8` | `SEEDING_ERROR` | The insertion of generated rows into the database failed. |

These codes are stable across versions and safe to use in scripts.
!!! note title="Stability notice"
These codes are early and might changes upon major future releases

## See also:
- [DSL Reference](dsl.md) — full language documentation
- [TUI Guide](quickstart.md) — how to use the interactive TUI
- [Configuration](configuration.md) — how to set up your config files
- [Generators](generators/index.md) — all available generators and their parameters
12 changes: 10 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,36 @@ Run it for as many rows as you need. Thanks to Faker every value is realistic, f

- **Framework-agnostic.** Works on any relational database, any stack.
- **Simple language.** If you know what a function call looks like, you know the Copia DSL.
- **Realistic data.** Built on [Faker](https://faker.readthedocs.io/) names, emails, addresses, IPs, dates, and more out of the box.
- **Realistic data.** Built on [Faker](https://faker.readthedocs.io/) names, emails, addresses, IPs, dates, and more out of the box.
- **Relational-aware.** The [`fetch()`](generators/fetch.md) generator lets you sample from existing rows, so foreign keys just work.
- **Interactive.** A full TUI lets you write, preview, and insert without leaving your terminal.

---

## Quick example

```
```title="users.copia"
# seed the users table
id: uuid()
username: username()
email: email()
birthdate: date_of_birth()
```

```title="posts.copia"
# seed the posts table — references existing users
id: uuid()
user_id: fetch('users.id')
body: paragraph()
date: past_date()
```

```bash
# check the result
copia run --dumps json users.copia
copia run --dumps json posts.copia
```

---

## Get started
Expand Down
4 changes: 2 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ copia --version

Copia come with supports for **MySQL and MariaDB** out of the box.

To use copia with other dbs, you can install the corresponding adapter optional dependency
To use copia with other dbs, you can install the corresponding adapter optional dependency.

for example on pip, the following command `pip install "copia-seed[postgres]"` will install the postgres adapter for copia
for example on pip, the following command `pip install "copia-seed[postgres]"` will install the dependency for postgres support.

### Additional adapters

Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ authors = [
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.11"
requires-python = ">=3.10"
dependencies = [
"faker>=40.8.0",
"lark>=1.3.1",
"pydantic>=2.12.5",
"pymysql>=1.1.2",
"textual>=8.2.1",
"tomlkit>=0.15.0",
"typer>=0.24.1",
]

Expand Down Expand Up @@ -60,4 +62,4 @@ src = ["src"]
line-length = 100

[tool.ruff.lint.per-file-ignores]
"*/__init__.py" = ["F401", "F403"]
"*/__init__.py" = ["F401", "F403"]
2 changes: 1 addition & 1 deletion src/copia/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.1"
__version__ = "0.3.2"
55 changes: 35 additions & 20 deletions src/copia/cli/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
from copia.parser.models import Column
from copia.runners import generate_rows, GeneratedRow
from copia.validator import SemanticValidator
from copia.generators import GENERATORS_REGISTRY

from copia.generators import (
GENERATORS_REGISTRY,
GeneratorValueError
)
from copia.cli.config import (
LOCAL_COPIA_FILE,
GLOBAL_COPIA_FILE
)

from ..utils import (
disable_consoles_output,
echo,
info,
error,
Expand All @@ -37,7 +40,24 @@ def main(

table: str | None = Option(None,
'--table', '-t',
help="the name of the table to commit to. mandatory without the --dumps flag."),
help="the name of the table to commit to. mandatory without the --dumps flag.",
rich_help_panel="Seeding options"),

skip_confirm: bool = Option(False,
'--skip-confirm', '-S',
rich_help_panel="Seeding options",
help="Skip the confirmation prompt. directly commit the values to the db."),

rows: int = Option(10,
'--rows', '-n',
help="the number of rows for the run.",
rich_help_panel="Generation options",
min=1),

dumps: FormatterId | None = Option(None,
'--dumps', '-d',
help="dumps the values based on the selected formatter.",
),

profile_name: str = Option('default',
'--profile', '-p',
Expand All @@ -54,28 +74,14 @@ def main(
help=f"search only in [green]'{LOCAL_COPIA_FILE}'[/].",
rich_help_panel="Config related options"),

dumps: FormatterId | None = Option(None,
'--dumps', '-d',
help="dumps the values based on the selected formatter.",
),

skip_config: bool = Option(False,
'--skip-config', '-s',
help="""disable config lookup for the run.
this will cause an error if the [green]'fetch'[/] generator is detected.
""",
rich_help_panel="Config related options"),

rows: int = Option(10,
'--rows', '-n',
help="the number of rows for the run.",
min=1),

skip_confirm: bool = Option(False,
'--skip-confirm', '-S',
help="Skip the confirmation prompt. directly commit the values to the db.")

):
):
"""Parse and run a file content

Ex:
Expand All @@ -95,6 +101,9 @@ def main(
error('--skip-config | -s flag is forbidden without --dumps | -d')
raise Exit(ExitCodes.BAD_CLI_USAGE)

if dumps:
disable_consoles_output()

content = file.read()
columns = parse_and_validate(content)
if skip_config:
Expand All @@ -105,9 +114,15 @@ def main(
try:
info("Generating values...")
generated_rows = list(row for row in generate_rows(adapter, columns, rows, column_notifier))
except Exception as err:
except (GeneratorValueError, ValueError) as err:
print_error(err)
raise Exit(ExitCodes.GENERATION_ERROR)
except KeyboardInterrupt:
info("Generation cancelled by user")
raise Exit()
except Exception as unexpected_err:
print_error(unexpected_err, 'this is an unexpected error (surely a bug), help us improve copia by reporting it!')
raise Exit(ExitCodes.UNEXPECTED_ERROR)

if not dumps:
assert adapter is not None
Expand All @@ -129,7 +144,7 @@ def column_notifier(column: str, values: list) -> None:
def dump_generated_values(formatter_id: FormatterId, values: list[GeneratedRow]) -> None:
formatter = get_formatter(formatter_id)
for formatted_row in formatter(values):
echo(formatted_row)
print(formatted_row)

def ask_and_seed(table: str , rows: list[GeneratedRow], adapter: BaseAdapter, preview_limit: int = 20) -> None:
columns = rows[0].keys()
Expand Down
7 changes: 6 additions & 1 deletion src/copia/cli/commands/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ def main(
"-l", "--local",
help=f"Search only in [green]'{LOCAL_COPIA_FILE}'")
):
"""Launch the interactive tui
"""Launch the interactive tui. require a valid configuration profile to run. Use --help for more options.

This command starts to the interactive TUI (Text User Interface) of Copia,
allowing you to generate data and manage your configurations in an interactive way.

You can specify a profile to use for the session, and choose to search for configurations in either the global or local configuration files.
"""

adapter = load_adapter_from_profile(profile_name, search_globals_only, search_locals_only)
Expand Down
Loading
Loading