Metadata-Version: 2.4
Name: anchovy
Version: 1.1.0
Summary: A minimal, unopinionated file processing engine intended for static website generation.
Author-email: Daniel Foerster <pydsigner@gmail.com>
License-Expression: Apache-2.0
Project-URL: Source, https://github.com/pydsigner/anchovy
Keywords: static,website,generation,html,css,template
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Pre-processors
Classifier: Topic :: Text Processing :: Markup
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: toml
Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "toml"
Provides-Extra: yaml
Requires-Dist: ruamel.yaml>=0.18.5; extra == "yaml"
Provides-Extra: jinja
Requires-Dist: Jinja2>=3.1.2; extra == "jinja"
Provides-Extra: markdown
Requires-Dist: anchovy[jinja]; extra == "markdown"
Requires-Dist: anchovy[toml]; extra == "markdown"
Requires-Dist: anchovy[yaml]; extra == "markdown"
Requires-Dist: markdown_it_py>=3.0.0; extra == "markdown"
Requires-Dist: mdit_py_plugins>=0.4.0; extra == "markdown"
Requires-Dist: Pygments>=2.12.0; extra == "markdown"
Provides-Extra: css
Requires-Dist: anchovy_css>=0.1.1; extra == "css"
Provides-Extra: pretty
Requires-Dist: rich>=12.5.1; extra == "pretty"
Provides-Extra: pillow
Requires-Dist: Pillow>=9.2.0; extra == "pillow"
Provides-Extra: minify
Requires-Dist: lightningcss<1.0,>=0.1.1; extra == "minify"
Requires-Dist: minify-html-onepass>=0.11.1; extra == "minify"
Requires-Dist: tdewolff-minify>=2.20.6; sys_platform != "darwin" and extra == "minify"
Provides-Extra: web
Requires-Dist: anchovy[markdown]; extra == "web"
Requires-Dist: anchovy[css]; extra == "web"
Requires-Dist: anchovy[pillow]; extra == "web"
Requires-Dist: anchovy[minify]; extra == "web"
Requires-Dist: lxml>=6.0.2; extra == "web"
Provides-Extra: include
Requires-Dist: requests>=2.31.0; extra == "include"
Requires-Dist: anchovy[toml]; extra == "include"
Provides-Extra: base
Requires-Dist: anchovy[web]; extra == "base"
Requires-Dist: anchovy[include]; extra == "base"
Requires-Dist: anchovy[pretty]; extra == "base"
Provides-Extra: all
Requires-Dist: anchovy[base]; extra == "all"
Provides-Extra: cq
Requires-Dist: anchovy[all]; extra == "cq"
Requires-Dist: tqdm>=4.65.0; extra == "cq"
Requires-Dist: minify-html>=0.11.1; extra == "cq"
Requires-Dist: pylint; extra == "cq"
Requires-Dist: pyright; extra == "cq"
Requires-Dist: pytest; extra == "cq"
Requires-Dist: pytest-cov; extra == "cq"
Dynamic: license-file

[![PyPI - Project Version](https://img.shields.io/pypi/v/anchovy)](https://pypi.org/project/anchovy)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/anchovy)](https://pypi.org/project/anchovy)
[![GitHub - Project License](https://img.shields.io/github/license/pydsigner/anchovy)](https://github.com/pydsigner/anchovy)
[![GitHub - Code Size](https://img.shields.io/github/languages/code-size/pydsigner/anchovy)](https://github.com/pydsigner/anchovy)
[![codecov](https://codecov.io/gh/pydsigner/anchovy/graph/badge.svg?token=A8WRBWO3JG)](https://codecov.io/gh/pydsigner/anchovy)

# Anchovy

Anchovy is a minimal, unopinionated file-processing framework equipped with a
complete static website generation toolkit.

* **Minimal:** Anchovy’s core is around a thousand lines of code and has no
  mandatory dependencies. Plus, Anchovy can be used for real projects with just
  a few pip-installable extras, even if you want to preprocess CSS.

* **Unopinionated:** Anchovy offers a set of components which can be easily
  configured to your site’s exact requirements, without tediously ripping out
  or overriding entrenched behaviors. Anchovy does not assume you are building
  a blog or that you wish to design your templates in a specific way. You can
  even build things that aren’t websites! Plus, Anchovy operates on files, so
  it’s simple to integrate tools like imagemagick, dart-sass, or less.js if you
  need them.

* **Complete:** Anchovy comes with a dependency auditing system, allowing you
  to grab any component you want without installing anything but Anchovy and
  find out what you *will* need to run your build. Choose from a wealth of
  Steps, Anchovy’s modular file processors, for everything from rendering Jinja
  templates and minifying CSS to unpacking archives and thumbnailing images.
  Plus, add a few extra parameters or lines of configuration to get automatic
  intelligent minimum builds based on input checksums, and get a reproducible
  run artifact to boot— even if you want to fetch HTTP resources or write your
  own Steps. Iterate quickly by launching a lightweight development-grade web
  server once the build is complete.

## Installation

Anchovy has no essential prerequisites and can be installed with
`pip install anchovy` to get just the framework and a few built-in components,
but for typical usage `pip install anchovy[base]` is recommended. This will
pull in support for Jinja2 templating, markdown, minification, and Anchovy’s
CSS preprocessor. A full list of available extras may be found in the
[pyproject.toml](https://github.com/pydsigner/anchovy/blob/master/pyproject.toml) file.

Alternatively, Anchovy may be installed directly from source with
`pip install git+https://github.com/pydsigner/anchovy` or the corresponding
`pip install git+https://github.com/pydsigner/anchovy#egg=anchovy[base]`.

## Command Line Usage

Anchovy operates on config files written in Python, or even modules directly.

* `python -m anchovy -h`
* `anchovy -m mypackage.anchovyconf -o ../release/`
* `python -m anchovy mysite/anchovy_site.py -h`

If `-h` is supplied with a module or config file, help will be displayed for
the pipeline configuration. If it is supplied without a module or config file,
help will be displayed for anchovy's own run-time options, such as
`--audit-steps` and `--serve`, as well as how to specify a module/config file.

### Show Me

Run `anchovy examples/code_index.py -s -p 8080`, then open a browser to
localhost:8080 (or click the link in the console). This example offers the most
extensive demonstration of Anchovy’s functionality as of version 1.0.

### What’s the Baseline?

Here’s minimal example performing about what the `staticjinja` markdown example
offers:

```python
from pathlib import Path

from anchovy import (
    DirectCopyStep,
    InputBuildSettings,
    JinjaMarkdownStep,
    OutputDirPathCalc,
    REMatcher,
    Rule,
)


# Optional, and can be overridden with CLI arguments.
SETTINGS = InputBuildSettings(
    input_dir=Path('site'),
    working_dir=Path('working'),
    output_dir=Path('build'),
    custody_cache=Path('build-cache.json'),
)
RULES = [
    # Ignore dotfiles found in either the input_dir or the working dir.
    Rule(
        (
            REMatcher(r'(.*/)*\..*', parent_dir='input_dir')
            | REMatcher(r'(.*/)*\..*', parent_dir='working_dir')
        ),
        None
    ),
    # Render markdown files, then stop processing them.
    Rule(
        REMatcher(r'.*\.md'),
        [OutputDirPathCalc('.html'), None],
        JinjaMarkdownStep()
    ),
    # Copy everything else in static/ directories through.
    Rule(
        REMatcher(r'(.*/)*static/.*', parent_dir='input_dir'),
        OutputDirPathCalc(),
        DirectCopyStep()
    ),
]
```

This example is very simple, but it’s legitimately enough to start with for a
small website, and offers an advantage over other minimal frameworks by putting
additional batteries within an arm’s reach. If we stored the configuration in
`config.py` and added a raw site like this:
```
site/
    static/
        styles.css
        toolbar.js
    base.jinja.html
    index.md
    about.md
    contact.md
```
 `python -m anchovy config.py` would produce output like this:
```
output/
    static/
        styles.css
        toolbar.js
    index.html
    about.html
    contact.html
```

This example can be found in runnable form as [examples/basic_site.py](https://github.com/pydsigner/anchovy/blob/master/examples/basic_site.py)
in the source distribution. Available command line arguments can be seen by
passing `-h`: `python -m anchovy examples/basic_site.py -h`.

## Programmatic Usage

Anchovy is very usable from the command line, but projects desiring to
customize behavior, for example by running tasks before or after pipeline
execution, may utilize `anchovy.cli.run_from_rules()`:

```python
import time
from pathlib import Path

from anchovy.cli import run_from_rules
from anchovy.core import Context

from my_site.config import SETTINGS, RULES


class MyContext(Context):
    def find_inputs(path: Path):
        # Only process files modified in the last hour.
        hour_ago = time.time() - 3600
        for candidate in super().find_inputs(path):
            if candidate.stat().st_mtime > hour_ago:
                yield candidate


def main():
    print('Pretending to run pre-pipeline tasks...')
    run_from_rules(SETTINGS, RULES, context_cls=MyContext)
    print('Pretending to run post-pipeline tasks...')


if __name__ == '__main__':
    main()
```
