diff --git a/.dev/Dockerfile b/.dev/Dockerfile index 6a661d1..e982946 100644 --- a/.dev/Dockerfile +++ b/.dev/Dockerfile @@ -1,21 +1,33 @@ FROM makukha/multipython:latest -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt,sharing=locked \ - --mount=type=bind,from=dattached/bootstrap,dst=/b \ -< Case conversion and verification for Python: snake_case, camelCase, kebab-case, etc. [![versions](https://img.shields.io/pypi/pyversions/caseutil.svg)](https://pypi.org/project/caseutil) -[![pypi](https://img.shields.io/pypi/v/caseutil.svg#v0.7.0)](https://pypi.python.org/pypi/caseutil) -[![Tests](https://raw.githubusercontent.com/makukha/caseutil/v0.7.0/docs/badge/tests.svg)](https://github.com/makukha/caseutil) -[![Coverage](https://raw.githubusercontent.com/makukha/caseutil/v0.7.0/docs/badge/coverage.svg)](https://github.com/makukha/caseutil) +[![pypi](https://img.shields.io/pypi/v/caseutil.svg#v0.7.1)](https://pypi.python.org/pypi/caseutil) +[![Tests](https://raw.githubusercontent.com/makukha/caseutil/v0.7.1/docs/badge/tests.svg)](https://github.com/makukha/caseutil) +[![Coverage](https://raw.githubusercontent.com/makukha/caseutil/v0.7.1/docs/badge/coverage.svg)](https://github.com/makukha/caseutil) [![PyPI - Downloads](https://img.shields.io/pypi/dw/caseutil)](https://pypistats.org/packages/caseutil) [![license](https://img.shields.io/github/license/makukha/caseutil.svg)](https://github.com/makukha/caseutil/blob/main/LICENSE) [![Documentation Status](https://readthedocs.org/projects/caseutil/badge/?version=latest)](https://caseutil.readthedocs.io/en/latest/?badge=latest) @@ -20,7 +20,14 @@ * No dependencies * 100% test coverage -### Supported cases +## Supported cases + +### [Classification](https://caseutil.readthedocs.io/en/latest/classification/) + +![Cases classification](docs/img/classification-dark.svg#gh-dark-mode-only) +![Cases classification](docs/img/classification-light.svg#gh-light-mode-only) + +### Simple functions | Case | Verify | Convert | |---------------|---------------|---------------| @@ -37,8 +44,6 @@ | Title Case | `is_title` | `to_title` | | Sentence case | `is_sentence` | `to_sentence` | -For more details about cases and their relations, see [Cases classification](https://caseutil.readthedocs.io/en/latest/classification/). - ## Installation ```shell @@ -65,7 +70,7 @@ hiThere seeYou ``` -## Simple usage +## Basic usage ```doctest >>> from caseutil import * @@ -151,15 +156,19 @@ Only ASCII names are supported. Unicode support is planned. This project requires [Docker](https://www.docker.com). ```shell -git clone https://github.com/makukha/caseutil.git -cd caseutil -task dev +$ git clone https://github.com/makukha/caseutil.git +$ cd caseutil +$ task dev ``` +In dev environment: + ```shell -root@caseutil:/project# task lint -root@caseutil:/project# task format -root@caseutil:/project# task test +$ task version -- minor +$ task build +$ task lint +$ task format +$ task test ``` ## Alternatives diff --git a/Taskfile.yml b/Taskfile.yml index 0d79164..c12708c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,6 +25,29 @@ tasks: cmds: - uv pip install --system -e . + build: + desc: Build artefacts + cmds: + - task: build:classification + - rm -rf dist + - mv README.md README.md.backup + - sed -e'{/#gh-dark-mode-only/ d; s|(docs/\(img/.*\)#gh-light-mode-only|(https://caseutil.readthedocs.io/en/latest/\1|;}' + README.md.backup > README.md + - uv build + - rm README.md + - mv README.md.backup README.md + + build:classification: + internal: true + sources: [docs/classification.md] + generates: + - docs/img/classification-dark.svg + - docs/img/classification-light.svg + cmds: + - sed -ne '/^```mermaid/,/^```/{/^```mermaid/d; s/^```//; p;}' docs/classification.md > /tmp/classification.mmd + - mmdc -p /root/puppeteer-config.json -i /tmp/classification.mmd -o docs/img/classification-dark.svg -t dark -b transparent 2>/dev/null + - mmdc -p /root/puppeteer-config.json -i /tmp/classification.mmd -o docs/img/classification-light.svg -t default -b transparent 2>/dev/null + docs: desc: Serve local MkDocs. cmds: @@ -51,8 +74,7 @@ tasks: preconditions: - test $(git rev-parse --abbrev-ref HEAD) = main cmds: - - rm -rf dist - - uv build + - task: build - uv publish # dev testing diff --git a/docs/badge/coverage.svg b/docs/badge/coverage.svg index 9eccd72..d032963 100644 --- a/docs/badge/coverage.svg +++ b/docs/badge/coverage.svg @@ -1,21 +1,21 @@ - + coverage: 100.00% - + - - - + + + - - coverage - - 100.00% + + coverage + + 100.00% diff --git a/docs/classification.md b/docs/classification.md index 149987b..ae51a46 100644 --- a/docs/classification.md +++ b/docs/classification.md @@ -2,24 +2,58 @@ The two properties bellow let us classify all widely used cases: *word separator* (underscore, hyphen, space, letter case change), and *word case rule*: -## Table - -| Words case rule | Underscore | Hyphen | Space | Case change | -|--------------------------|---------------|--------------|-----------------|---------------| -| all words are `lower` | `snake_case` | `kebab-case` | `lower case` | ∅2 | -| 1st `lower` rest `Title` | —1 | — | — | `camelCase` | -| all words are `Title` | `Ada_Case` | `Train-Case` | `Title Case` | `PascalCase` | -| 1st `Title` rest `lower` | — | — | `Sentence case` | ∅ | -| all words are `UPPER` | `CONST_CASE` | `COBOL-CASE` | `UPPER CASE` | ∅ | - -1 not widely used, 2 not possible +```mermaid +block-beta +columns 6 + W["Word case"]:2 + D["Delimiter"]:4 + + FW["First word"] RW["Other words"] + UD["underscore"] HD["hyphen"] SD["space"] CD["case change"] + + FW1["lower"] RW1["lower"] + snake(["snake_case"]) kebab(["kebab-case"]) lower(["lower case"]) llc("∅") + + FW2["lower"] RW2["Title"] + ltu("—") lth("—") lts("—") camel(["camelCase"]) + + FW3["Title"] RW3["Title"] + ada(["Ada_Case"]) train(["Train-Case"]) title(["Title Case"]) pascal(["PascalCase"]) + + FW4["Title"] RW4["lower"] + tlu("—") tlh("—") sentence(["Sentence case"]) tlc("∅") + + FW5["UPPER"] RW5["UPPER"] + const(["CONST_CASE"]) cobol(["COBOL-CASE"]) upper(["UPPER CASE"]) uuc("∅") + + classDef Head1 fill:#006400,fill-opacity:0.9,color:white,stroke:white,stroke-width:1px; + classDef Head2 fill:#228B22,fill-opacity:0.9,color:white,stroke:white,stroke-width:1px; + classDef Empty fill-opacity:0,stroke-width:0px; + + classDef CaseL padding-left:8px,padding-right:8px,fill:#FFB6C1,fill-opacity:0.2,stroke-width:0px,font-weight:bold; + classDef CaseT padding-left:8px,padding-right:8px,fill:#00BFFF,fill-opacity:0.2,stroke-width:0px,font-weight:bold; + classDef CaseU padding-left:8px,padding-right:8px,fill:#00FFFF,fill-opacity:0.2,stroke-width:0px,font-weight:bold; + + class W,FW,RW,D Head1 + class FW1,RW1,FW2,RW2,FW3,RW3,FW4,RW4,FW5,RW5,UD,HD,SD,CD Head2 + class snake,kebab,lower,camel CaseL + class ada,train,title,pascal,sentence CaseT + class const,cobol,upper CaseU + class llc,ltu,lth,lts,tlu,tlh,tlc,uuc Empty +``` + +* `—` not widely used +* `∅` not possible ## Ambiguity -It is easy to observe that when there is a single word (no separators possible), all 12 cases are reduced to 3 classes: +1. When there is a single word (no separators possible), all 12 cases reduce to 3 classes: + * `lower` = `camel` = `kebab` = `snake` + * `Title` = `Ada` = `Pascal` = `Sentence` = `Train` + * `UPPER` = `COBOL` = `CONST` -* `lower` = `camel` = `kebab` = `snake` -* `Title` = `Ada` = `Pascal` = `Sentence` = `Train` -* `UPPER` = `COBOL` = `CONST` +2. When there is a single character (Title and UPPER match), all 12 cases reduce to 2 classes: + * `lower` = `camel` = `kebab` = `snake` + * `Title` = `Ada` = `Pascal` = `Sentence` = `Train` = `UPPER` = `COBOL` = `CONST` -This makes case detection multivalued when there is more than one word. +This makes case detection multivalued when there is a single word or single character. diff --git a/docs/img/classification-dark.svg b/docs/img/classification-dark.svg new file mode 100644 index 0000000..e83648f --- /dev/null +++ b/docs/img/classification-dark.svg @@ -0,0 +1 @@ +
Word case
Delimiter
First word
Other words
underscore
hyphen
space
case change
lower
lower
snake_case
kebab-case
lower case
lower
Title
camelCase
Title
Title
Ada_Case
Train-Case
Title Case
PascalCase
Title
lower
Sentence case
UPPER
UPPER
CONST_CASE
COBOL-CASE
UPPER CASE
\ No newline at end of file diff --git a/docs/img/classification-light.svg b/docs/img/classification-light.svg new file mode 100644 index 0000000..0c84974 --- /dev/null +++ b/docs/img/classification-light.svg @@ -0,0 +1 @@ +
Word case
Delimiter
First word
Other words
underscore
hyphen
space
case change
lower
lower
snake_case
kebab-case
lower case
lower
Title
camelCase
Title
Title
Ada_Case
Train-Case
Title Case
PascalCase
Title
lower
Sentence case
UPPER
UPPER
CONST_CASE
COBOL-CASE
UPPER CASE
\ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 558b283..5d47aac 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,9 +2,9 @@ > Case conversion and verification for Python: snake_case, camelCase, kebab-case, etc. [![versions](https://img.shields.io/pypi/pyversions/caseutil.svg)](https://pypi.org/project/caseutil) -[![pypi](https://img.shields.io/pypi/v/caseutil.svg#v0.7.0)](https://pypi.python.org/pypi/caseutil) -[![Tests](https://raw.githubusercontent.com/makukha/caseutil/v0.7.0/docs/badge/tests.svg)](https://github.com/makukha/caseutil) -[![Coverage](https://raw.githubusercontent.com/makukha/caseutil/v0.7.0/docs/badge/coverage.svg)](https://github.com/makukha/caseutil) +[![pypi](https://img.shields.io/pypi/v/caseutil.svg#v0.7.1)](https://pypi.python.org/pypi/caseutil) +[![Tests](https://raw.githubusercontent.com/makukha/caseutil/v0.7.1/docs/badge/tests.svg)](https://github.com/makukha/caseutil) +[![Coverage](https://raw.githubusercontent.com/makukha/caseutil/v0.7.1/docs/badge/coverage.svg)](https://github.com/makukha/caseutil) [![PyPI - Downloads](https://img.shields.io/pypi/dw/caseutil)](https://pypistats.org/packages/caseutil) [![license](https://img.shields.io/github/license/makukha/caseutil.svg)](https://github.com/makukha/caseutil/blob/main/LICENSE) [![Documentation Status](https://readthedocs.org/projects/caseutil/badge/?version=latest)](https://caseutil.readthedocs.io/en/latest/?badge=latest) @@ -20,7 +20,14 @@ * No dependencies * 100% test coverage -### Supported cases +## Supported cases + +![Cases classification](img/classification-dark.svg#only-dark) +![Cases classification](img/classification-light.svg#only-light) + +See [classification details](classification.md). + +### Simple functions | Case | Verify | Convert | |---------------|---------------|---------------| @@ -37,26 +44,12 @@ | Title Case | `is_title` | `to_title` | | Sentence case | `is_sentence` | `to_sentence` | -For more details about cases and their relations, see [Cases classification](classification.md). - ## Installation ```shell $ pip install caseutil ``` -## Simple usage - -```doctest ->>> from caseutil import * - ->>> is_snake('Foo bar-baz') -False - ->>> to_snake('Foo bar-baz') -'foo_bar_baz' -``` - ## Command line ```shell @@ -77,6 +70,18 @@ hiThere seeYou ``` +## Basic usage + +```doctest +>>> from caseutil import * + +>>> is_snake('Foo bar-baz') +False + +>>> to_snake('Foo bar-baz') +'foo_bar_baz' +``` + ## Advanced usage ### Cases enum diff --git a/mkdocs.yml b/mkdocs.yml index 6724b24..afb6eb5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,7 +21,11 @@ extra_css: markdown_extensions: - admonition - pymdownx.details - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format theme: name: material diff --git a/pyproject.toml b/pyproject.toml index ee21352..f29aa17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ # NOTE: Keep in sync with setup.cfg name = "caseutil" -version = "0.7.0" +version = "0.7.1" description = "Case convert and verify for Python: snake_case, camelCase, kebab-case, and more." authors = [{name = "Michael Makukha", email = "m.makukha@gmail.com"}] readme = "README.md" @@ -55,7 +55,7 @@ Changelog = "https://github.com/makukha/caseutil/releases" # bump-my-version [tool.bumpversion] -current_version = "0.7.0" +current_version = "0.7.1" allow_dirty = true files = [ {filename = "docs/index.md"}, diff --git a/setup.cfg b/setup.cfg index 4419698..5b66cc0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,8 +4,9 @@ # NOTE: Keep in sync with pyproject.toml (version is updated automatically) name = caseutil -version = 0.7.0 -description = Case convert and verify for Python: snake_case, camelCase, kebab-case, and more. +version = 0.7.1 +description = Case conversion and verification for Python: snake_case, camelCase, kebab-case, etc. +long_description = file: README.md, LICENSE author = Michael Makukha author_email = m.makukha@gmail.com diff --git a/src/caseutil/__version__.py b/src/caseutil/__version__.py index a71c5c7..f0788a8 100644 --- a/src/caseutil/__version__.py +++ b/src/caseutil/__version__.py @@ -1 +1 @@ -__version__ = '0.7.0' +__version__ = '0.7.1'