Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Note that for version number starting with a `0`, i.e., `0.x.y`, a bump of `x`
should be considered as a major (and thus potentially breaking) change. See
semver guidelines for more details about this.

## [Unreleased]
- Python fallback client: removed `--compatibility-mode` / `-c` flag. Its original purpose (output mimicking Unix `file`) was lost when the `magic` field was removed in v0.6.1; it had since been a no-op alias for `--no-colors`.
- Python fallback client: colors are now auto-disabled when stdout is not a TTY, matching Rust client behavior. Use `--colors` to force them on, `--no-colors` to force them off.

## [1.0.2] - 2026-02-25
- Mark python 3.14 as supported.
- Remove direct dependency on numpy.
Expand Down
43 changes: 24 additions & 19 deletions python/src/magika/cli/magika_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,6 @@
is_flag=True,
help="Output a simple label instead of a verbose content type description. Use --list-output-content-types for the list of supported output.",
)
@click.option(
"-c",
"--compatibility-mode",
"magic_compatibility_mode",
is_flag=True,
help="Compatibility mode: output is as close as possible to `file` and colors are disabled.",
)
@click.option(
"-s",
"--output-score",
Expand All @@ -112,11 +105,18 @@
help="This option causes symlinks not to be followed. By default, symlinks are dereferenced.",
)
@click.option(
"--colors/--no-colors",
"with_colors",
"--colors",
"force_colors",
is_flag=True,
default=False,
help="Force color output regardless of terminal support.",
)
@click.option(
"--no-colors",
"no_colors",
is_flag=True,
default=True,
help="Enable/disable use of colors.",
default=False,
help="Disable color output regardless of terminal support.",
)
@click.option("-v", "--verbose", is_flag=True, help="Enable more verbose output.")
@click.option("-vv", "--debug", is_flag=True, help="Enable debug logging.")
Expand All @@ -137,12 +137,12 @@ def main(
jsonl_output: bool,
mime_output: bool,
label_output: bool,
magic_compatibility_mode: bool,
output_score: bool,
prediction_mode_str: str,
batch_size: int,
no_dereference: bool,
with_colors: bool,
force_colors: bool,
no_colors: bool,
verbose: bool,
debug: bool,
output_version: bool,
Expand All @@ -154,9 +154,16 @@ def main(
# the argument "file" (which is ugly) and we re-assign it as soon as we can.
files_paths = file

if magic_compatibility_mode:
# In compatibility mode we disable colors.
if force_colors and no_colors:
click.echo("Error: --colors and --no-colors are mutually exclusive.", err=True)
sys.exit(1)

if force_colors:
with_colors = True
elif no_colors:
with_colors = False
else:
with_colors = sys.stdout.isatty()

_l = get_logger(use_colors=with_colors)

Expand Down Expand Up @@ -198,8 +205,8 @@ def main(
_l.error("You should use either --json or --jsonl, not both.")
sys.exit(1)

if int(mime_output) + int(label_output) + int(magic_compatibility_mode) > 1:
_l.error("You should use only one of --mime, --label, --compatibility-mode.")
if int(mime_output) + int(label_output) > 1:
_l.error("You should use only one of --mime-type, --label.")
sys.exit(1)

if recursive:
Expand Down Expand Up @@ -283,8 +290,6 @@ def main(
for file_path, result in zip(batch_files_paths, batch_predictions):
if result.ok:
if mime_output:
# If the user requested the MIME type, we use the mime type
# regardless of the compatibility mode.
output = result.prediction.output.mime_type
elif label_output:
output = str(result.prediction.output.label)
Expand Down
38 changes: 29 additions & 9 deletions python/tests/test_python_magika_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,39 @@
# limitations under the License.

import subprocess
import sys
from pathlib import Path

CLIENT = (
Path(__file__).parent.parent / "src" / "magika" / "cli" / "magika_client.py"
).resolve()


def _run(*args: str) -> subprocess.CompletedProcess:
return subprocess.run(
[sys.executable, str(CLIENT), *args], capture_output=True, text=True
)

def test_python_magika_client() -> None:
python_root_dir = Path(__file__).parent.parent
python_magika_client_path = (
python_root_dir / "src" / "magika" / "cli" / "magika_client.py"
).resolve()

def test_python_magika_client() -> None:
# quick test to check there are no obvious problems
cmd = [str(python_magika_client_path), "--help"]
subprocess.run(cmd, capture_output=True, check=True)
subprocess.run([sys.executable, str(CLIENT), "--help"], capture_output=True, check=True)

# quick test to check there are no crashes
cmd = [str(python_magika_client_path), str(python_magika_client_path)]
subprocess.run(cmd, capture_output=True, check=True)
subprocess.run([sys.executable, str(CLIENT), str(CLIENT)], capture_output=True, check=True)


def test_compatibility_mode_removed() -> None:
result = _run("--compatibility-mode", str(CLIENT))
assert result.returncode != 0, "--compatibility-mode should no longer be accepted"


def test_no_colors_flag() -> None:
result = _run("--no-colors", str(CLIENT))
assert result.returncode == 0
assert "\x1b[" not in result.stdout, "--no-colors should produce no ANSI escape codes"


def test_colors_flag() -> None:
result = _run("--colors", str(CLIENT))
assert result.returncode == 0