jsonid


Namejsonid JSON
Version 0.8.1 PyPI version JSON
download
home_pageNone
Summaryjsonid a json identification tool
upload_time2025-07-15 18:21:28
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseNone
keywords json digipres file-formats
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # jsonid

<!-- markdownlint-disable -->
<img
    src="https://github.com/ffdev-info/jsonid/blob/main/static/images/JSON_logo-crockford.png?raw=true"
    alt="JSON ID logo based on JSON Logo by Douglas Crockford"
    width="200px" />
<!-- markdownlint-enable -->

**[JSON][json-1]ID**entification tool and ruleset. `jsonid` can be downloaded
from pypi.org.

[![PyPI - Version](https://img.shields.io/pypi/v/jsonid?style=plastic&color=purple)][pypi-json-id-1]

[json-1]: https://www.json.org/json-en.html
[pypi-json-id-1]: https://pypi.org/project/jsonid/

## Function

`jsonid` borrows from the Python approach to ask forgiveness rather than
permission (EAFP) to attempt to open every object it scans and see if it
parses as JSON. If it doesn't, we move along. If it does, we then have an
opportunity to identify the characteristics of the JSON we have opened.

Python being high-level also provides an easier path to processing files
and parsing JSON quickly with very little other knowledge required
of the underlying data structure.

## Why?

Consider these equivalent forms:

```json
{
    "key 1": "value",
    "key 2": "value"
}
```

```json
{
    "key 2": "value",
    "key 1": "value"
}
```

PRONOM signatures are not expressive enough for complicated JSON objects.

If I want DROID to find `key 1` I have to use a wildcard, so I would write
something like:

```text
BOF: "7B*226B6579203122"
EOF: "7D"
```

But if I then want to match on `key 2` as well as `key 1` things start getting
complicated as they aren't guaranteed by the JSON specification to be in the
same "position" (if we think about order visually). When other keys are used in
the object they aren't even guaranteed to be next to one another.

This particular example is a 'map' object whose most important property
is consistent retrieval of information through its "keys". Further
complexity can be added when we are dealing with maps embedded in a "list" or
"array", or simply just maps of arbitrary depth.

`jsonid` tries to compensate for JSON's complexities by using the format's
own strengths to parse binary data as JSON and then if is successful,
use a JSON-inspired grammar to describe keys and key-value pairs as "markers"
that can potentially identify the JSON objects that we are looking at.
Certainly narrow down the potential instances of JSON objects that we might
be looking at.

## What does `jsonid` get you?

To begin, `jsonid` should identify JSON files on your system as JSON.
That's already a pretty good position to be in.

The ruleset should then allow you to identify a decent number of JSON objects,
especially those that have a well-defined structure. Examples we have in the
[registry data][registry-htm-1] include things like ActivityPub streams,
RO-CRATE metadata, IIIF API data and so on.

If the ruleset works for JSON we might be able to apply it to other formats
that can represent equivalent data structures in the future
such as [YAML][yaml-spec], and [TOML][toml-spec].

[yaml-spec]: https://yaml.org/
[toml-spec]: https://toml.io/en/

## Ruleset

`jsonid` currently defines a small set of rules that help us to identify JSON
documents.

The rules are described in their own data-structures. The structures are
processed as a list (they need not necessarily be in order) and each must
match for a given set of ruls to determine what kind of JSON document we might
be looking at.

`jsonid` can identify the existence of information but you can also use
wildcards and provide some negation as required, e.g. to remove
false-positives between similar JSON entities.

| rule       | meaning                                               |
|------------|-------------------------------------------------------|
| INDEX      | index (from which to read when structure is an array) |
| GOTO       | goto key (read key at given key)                      |
| KEY        | key to read                                           |
| CONTAINS   | value contains string                                 |
| STARTSWITH | value startswith string                               |
| ENDSWITH   | value endswith string                                 |
| IS         | value matches exactly                                 |
| REGEX      | value matches a regex pattern                         |
| EXISTS     | key exists                                            |
| NOEXIST    | key doesn't exists                                    |
| ISTYPE     | key is a specific type (string, number, dict, array)  |

Stored in a list within a `RegistryEntry` object, they are then processed
in order.

For example:

```json
    [
        { "KEY": "name", "IS": "value" },
        { "KEY": "schema", "CONTAINS": "/schema/version/1.1/" },
        { "KEY": "data", "IS": { "more": "data" } },
    ]
```

All rules need to match for a positive ID.

> **NB.**: `jsonid` is a
work-in-progress and requires community input to help determine the grammar
in its fullness and so there is a lot of opportunity to add/remove to these
methods as its development continues. Additionally, help formalizing the
grammar/ruleset would be greatly appreciated 🙏.

### Backed by testing

The ruleset has been developed using test-driven-development practices (TDD)
and the current set of tests can be reviewed in the repository's
[test folder][testing-1]. More tests should be added, in general, and over
time.

[testing-1]: https://github.com/ffdev-info/jsonid/tree/main/tests

## Registry

A temporary "registry" module is used to store JSON markers.
The registry is a work in progress and must be exported and
rewritten somewhere more centralized (and easier to manage) if `jsonid` can
prove useful to the communities that might use it (*see notes on PRONOM below*).

The registry web-page is here:

* [jsonid registry][registry-htm-1].

[registry-htm-1]: https://ffdev-info.github.io/jsonid/registry/

The registry's source is here:

* [Registry](https://github.com/ffdev-info/jsonid/blob/main/src/jsonid/registry_data.py).

### Registry examples

#### Identifying JSON-LD Generic

```python
    RegistryEntry(
        identifier="id0009",
        name=[{"@en": "JSON-LD (generic)"}],
        markers=[
            {"KEY": "@context", "EXISTS": None},
            {"KEY": "id", "EXISTS": None},
        ],
    ),
```

> **Pseudo code**:
Test for the existence of keys: `@context` and `id` in the primary JSON object.

#### Identifying Tika Recursive Metadata

```python
    RegistryEntry(
        identifier="id0024",
        name=[{"@en": "tika recursive metadata"}],
        markers=[
            {"INDEX": 0, "KEY": "Content-Length", "EXISTS": None},
            {"INDEX": 0, "KEY": "Content-Type", "EXISTS": None},
            {"INDEX": 0, "KEY": "X-TIKA:Parsed-By", "EXISTS": None},
            {"INDEX": 0, "KEY": "X-TIKA:parse_time_millis", "EXISTS": None},
        ],
```

> **Pseudo code**:
Test for the existence of keys: `Content-Length`, `Content-Type`,
`X-TIKA:Parsed-By` and `X-TIKA:parse_time_millis` in the `zeroth` (first)
JSON object where the primary document is a list of JSON objects.

#### Identifying SOPS encrypted secrets file

```python
    RegistryEntry(
        identifier="id0012",
        name=[{"@en": "sops encrypted secrets file"}],
        markers=[
            {"KEY": "sops", "EXISTS": None},
            {"GOTO": "sops", "KEY": "kms", "EXISTS": None},
            {"GOTO": "sops", "KEY": "pgp", "EXISTS": None},
        ],
    ),
```

> **Pseudo code**:
Test for the existence of keys `sops` in the primary JSON object.
>
> Goto the `sops` key and test for the existence of keys: `kms` and `pgp`
within the `sops` object/value.

### Local rules

The plan is to allow local rules to be run alongside the global ruleset. I
expect this will be a bit further down the line when the ruleset and
metaddata is more stabilised.

## PRONOM

Ideally `jsonid` can generate evidence enough to warrant the creration of
PRONOM IDs that can then be referenced in the `jsonid` output.

Evantually, PRONOM or a PRONOM-like tool might host an authoritative version
of the `jsonid` registry.

## Output format

For ease of development, the utility currently outputs `yaml`. The structure
is still very fluid, and will also vary depending on the desired level of
detail in the registry, e.g. there isn't currently a lot of information about
the contents beyond a basic title and identifier.

E.g.:

```yaml
---
jsonid: 0.0.0
scandate: 2025-04-21T18:40:48Z
---
file: integration_files/plain.json
additional:
- '@en': data is dict type
depth: 1
documentation:
- archive_team: http://fileformats.archiveteam.org/wiki/JSON
identifiers:
- rfc: https://datatracker.ietf.org/doc/html/rfc8259
- pronom: http://www.nationalarchives.gov.uk/PRONOM/fmt/817
- loc: https://www.loc.gov/preservation/digital/formats/fdd/fdd000381.shtml
- wikidata: https://www.wikidata.org/entity/Q2063
mime:
- application/json
name:
- '@en': JavaScript Object Notation (JSON)
---
```

The structure should become more concrete as `jsonid` is formalized.

## Analysis

`jsonid` provides an analysis mechanism to help developers of identifiers. It
might also help users talk about interesting properties about the objects
being analysed, and provide consistent fingerprinting for data that has
different byte-alignment but is otherwise identical.

> **NB.**: Comments on existing statistics or ideas for new ones are
appreciated.

### Example analysis

```json
{
  "content_length": 329,
  "number_of_lines": 32,
  "line_warning": false,
  "top_level_keys_count": 4,
  "top_level_keys": [
    "key1",
    "key2",
    "key3",
    "key4"
  ],
  "top_level_types": [
    "list",
    "map",
    "list",
    "list"
  ],
  "depth": 8,
  "heterogeneous_list_types": true,
  "fingerprint": {
    "unf": "UNF:6:sAsKNmjOtnpJtXi3Q6jVrQ==",
    "cid": "bafkreibho6naw5r7j23gxu6rzocrud4pc6fjsnteyjveirtnbs3uxemv2u"
  },
  "encoding": "UTF-8"
}
```

## Utils

### json2json

UTF-16 can be difficult to read as UTF-16 uses two bytes per every one, e.g.
`..{.".a.".:. .".b.".}.` is simply `{"a": "b"}`. The utility `json2json.py`
in the utils folder will output UTF-16 as UTF-8 so that signatures can be
more easily derived. A signature derived for UTF-16 looks exactly the same
as UTF-8.

`json2json` can be called from the command line when installed via pip, or
find it in [src.utils][json2json-1].

[json2json-1]: src/utils/json2json.py

## Docs

Dev docs are [available][dev-docs-1].

[dev-docs-1]: https://ffdev-info.github.io/jsonid/jsonid/

----

## Developer install

### pip

Setup a virtual environment `venv` and install the local development
requirements as follows:

```bash
python3 -m venv venv
source venv/bin/activate
python -m pip install -r requirements/local.txt
```

### tox

#### Run tests (all)

```bash
python -m tox
```

#### Run tests-only

```bash
python -m tox -e py3
```

#### Run linting-only

```bash
python -m tox -e linting
```

### pre-commit

Pre-commit can be used to provide more feedback before committing code. This
reduces reduces the number of commits you might want to make when working on
code, it's also an alternative to running tox manually.

To set up pre-commit, providing `pip install` has been run above:

* `pre-commit install`

This repository contains a default number of pre-commit hooks, but there may
be others suited to different projects. A list of other pre-commit hooks can be
found [here][pre-commit-1].

[pre-commit-1]: https://pre-commit.com/hooks.html

## Packaging

The [`justfile`][just-1] contains helper functions for packaging and release.
Run `just help` for more information.

[just-1]: https://github.com/casey/just

### pyproject.toml

Packaging consumes the metadata in `pyproject.toml` which helps to describe
the project on the official [pypi.org][pypi-2] repository. Have a look at the
documentation and comments there to help you create a suitably descriptive
metadata file.

### Versioning

Versioning in Python can be hit and miss. You can label versions for
yourself, but to make it reliaable, as well as meaningful is should be
controlled by your source control system. We assume git, and versions can
be created by tagging your work and pushing the tag to your git repository,
e.g. to create a release candidate for version 1.0.0:

```sh
git tag -a 1.0.0-rc.1 -m "release candidate for 1.0.0"
git push origin 1.0.0-rc.1
```

When you build, a package will be created with the correct version:

```sh
just package-source
### build process here ###
Successfully built python_repo_jsonid-1.0.0rc1.tar.gz and python_repo_jsonid-1.0.0rc1-py3-none-any.whl
```

### Local packaging

To create a python wheel for testing locally, or distributing to colleagues
run:

* `just package-source`

A `tar` and `whl` file will be stored in a `dist/` directory. The `whl` file
can be installed as follows:

* `pip install <your-package>.whl`

### Publishing

Publishing for public use can be achieved with:

* `just package-upload-test` or `just package-upload`

`just-package-upload-test` will upload the package to [test.pypi.org][pypi-1]
which provides a way to look at package metadata and documentation and ensure
that it is correct before uploading to the official [pypi.org][pypi-2]
repository using `just package-upload`.

[pypi-1]: https://test.pypi.org
[pypi-2]: https://pypi.org

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "jsonid",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "\"R. Spencer\" <all.along.the.watchtower2001+github@gmail.com>",
    "keywords": "json, digipres, file-formats",
    "author": null,
    "author_email": "\"R. Spencer\" <all.along.the.watchtower2001+github@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/52/68/ce4c3e707834e78848ac8c9e72aff99b09ab9eeb3f4b148096dcf594f658/jsonid-0.8.1.tar.gz",
    "platform": null,
    "description": "# jsonid\n\n<!-- markdownlint-disable -->\n<img\n    src=\"https://github.com/ffdev-info/jsonid/blob/main/static/images/JSON_logo-crockford.png?raw=true\"\n    alt=\"JSON ID logo based on JSON Logo by Douglas Crockford\"\n    width=\"200px\" />\n<!-- markdownlint-enable -->\n\n**[JSON][json-1]ID**entification tool and ruleset. `jsonid` can be downloaded\nfrom pypi.org.\n\n[![PyPI - Version](https://img.shields.io/pypi/v/jsonid?style=plastic&color=purple)][pypi-json-id-1]\n\n[json-1]: https://www.json.org/json-en.html\n[pypi-json-id-1]: https://pypi.org/project/jsonid/\n\n## Function\n\n`jsonid` borrows from the Python approach to ask forgiveness rather than\npermission (EAFP) to attempt to open every object it scans and see if it\nparses as JSON. If it doesn't, we move along. If it does, we then have an\nopportunity to identify the characteristics of the JSON we have opened.\n\nPython being high-level also provides an easier path to processing files\nand parsing JSON quickly with very little other knowledge required\nof the underlying data structure.\n\n## Why?\n\nConsider these equivalent forms:\n\n```json\n{\n    \"key 1\": \"value\",\n    \"key 2\": \"value\"\n}\n```\n\n```json\n{\n    \"key 2\": \"value\",\n    \"key 1\": \"value\"\n}\n```\n\nPRONOM signatures are not expressive enough for complicated JSON objects.\n\nIf I want DROID to find `key 1` I have to use a wildcard, so I would write\nsomething like:\n\n```text\nBOF: \"7B*226B6579203122\"\nEOF: \"7D\"\n```\n\nBut if I then want to match on `key 2` as well as `key 1` things start getting\ncomplicated as they aren't guaranteed by the JSON specification to be in the\nsame \"position\" (if we think about order visually). When other keys are used in\nthe object they aren't even guaranteed to be next to one another.\n\nThis particular example is a 'map' object whose most important property\nis consistent retrieval of information through its \"keys\". Further\ncomplexity can be added when we are dealing with maps embedded in a \"list\" or\n\"array\", or simply just maps of arbitrary depth.\n\n`jsonid` tries to compensate for JSON's complexities by using the format's\nown strengths to parse binary data as JSON and then if is successful,\nuse a JSON-inspired grammar to describe keys and key-value pairs as \"markers\"\nthat can potentially identify the JSON objects that we are looking at.\nCertainly narrow down the potential instances of JSON objects that we might\nbe looking at.\n\n## What does `jsonid` get you?\n\nTo begin, `jsonid` should identify JSON files on your system as JSON.\nThat's already a pretty good position to be in.\n\nThe ruleset should then allow you to identify a decent number of JSON objects,\nespecially those that have a well-defined structure. Examples we have in the\n[registry data][registry-htm-1] include things like ActivityPub streams,\nRO-CRATE metadata, IIIF API data and so on.\n\nIf the ruleset works for JSON we might be able to apply it to other formats\nthat can represent equivalent data structures in the future\nsuch as [YAML][yaml-spec], and [TOML][toml-spec].\n\n[yaml-spec]: https://yaml.org/\n[toml-spec]: https://toml.io/en/\n\n## Ruleset\n\n`jsonid` currently defines a small set of rules that help us to identify JSON\ndocuments.\n\nThe rules are described in their own data-structures. The structures are\nprocessed as a list (they need not necessarily be in order) and each must\nmatch for a given set of ruls to determine what kind of JSON document we might\nbe looking at.\n\n`jsonid` can identify the existence of information but you can also use\nwildcards and provide some negation as required, e.g. to remove\nfalse-positives between similar JSON entities.\n\n| rule       | meaning                                               |\n|------------|-------------------------------------------------------|\n| INDEX      | index (from which to read when structure is an array) |\n| GOTO       | goto key (read key at given key)                      |\n| KEY        | key to read                                           |\n| CONTAINS   | value contains string                                 |\n| STARTSWITH | value startswith string                               |\n| ENDSWITH   | value endswith string                                 |\n| IS         | value matches exactly                                 |\n| REGEX      | value matches a regex pattern                         |\n| EXISTS     | key exists                                            |\n| NOEXIST    | key doesn't exists                                    |\n| ISTYPE     | key is a specific type (string, number, dict, array)  |\n\nStored in a list within a `RegistryEntry` object, they are then processed\nin order.\n\nFor example:\n\n```json\n    [\n        { \"KEY\": \"name\", \"IS\": \"value\" },\n        { \"KEY\": \"schema\", \"CONTAINS\": \"/schema/version/1.1/\" },\n        { \"KEY\": \"data\", \"IS\": { \"more\": \"data\" } },\n    ]\n```\n\nAll rules need to match for a positive ID.\n\n> **NB.**: `jsonid` is a\nwork-in-progress and requires community input to help determine the grammar\nin its fullness and so there is a lot of opportunity to add/remove to these\nmethods as its development continues. Additionally, help formalizing the\ngrammar/ruleset would be greatly appreciated \ud83d\ude4f.\n\n### Backed by testing\n\nThe ruleset has been developed using test-driven-development practices (TDD)\nand the current set of tests can be reviewed in the repository's\n[test folder][testing-1]. More tests should be added, in general, and over\ntime.\n\n[testing-1]: https://github.com/ffdev-info/jsonid/tree/main/tests\n\n## Registry\n\nA temporary \"registry\" module is used to store JSON markers.\nThe registry is a work in progress and must be exported and\nrewritten somewhere more centralized (and easier to manage) if `jsonid` can\nprove useful to the communities that might use it (*see notes on PRONOM below*).\n\nThe registry web-page is here:\n\n* [jsonid registry][registry-htm-1].\n\n[registry-htm-1]: https://ffdev-info.github.io/jsonid/registry/\n\nThe registry's source is here:\n\n* [Registry](https://github.com/ffdev-info/jsonid/blob/main/src/jsonid/registry_data.py).\n\n### Registry examples\n\n#### Identifying JSON-LD Generic\n\n```python\n    RegistryEntry(\n        identifier=\"id0009\",\n        name=[{\"@en\": \"JSON-LD (generic)\"}],\n        markers=[\n            {\"KEY\": \"@context\", \"EXISTS\": None},\n            {\"KEY\": \"id\", \"EXISTS\": None},\n        ],\n    ),\n```\n\n> **Pseudo code**:\nTest for the existence of keys: `@context` and `id` in the primary JSON object.\n\n#### Identifying Tika Recursive Metadata\n\n```python\n    RegistryEntry(\n        identifier=\"id0024\",\n        name=[{\"@en\": \"tika recursive metadata\"}],\n        markers=[\n            {\"INDEX\": 0, \"KEY\": \"Content-Length\", \"EXISTS\": None},\n            {\"INDEX\": 0, \"KEY\": \"Content-Type\", \"EXISTS\": None},\n            {\"INDEX\": 0, \"KEY\": \"X-TIKA:Parsed-By\", \"EXISTS\": None},\n            {\"INDEX\": 0, \"KEY\": \"X-TIKA:parse_time_millis\", \"EXISTS\": None},\n        ],\n```\n\n> **Pseudo code**:\nTest for the existence of keys: `Content-Length`, `Content-Type`,\n`X-TIKA:Parsed-By` and `X-TIKA:parse_time_millis` in the `zeroth` (first)\nJSON object where the primary document is a list of JSON objects.\n\n#### Identifying SOPS encrypted secrets file\n\n```python\n    RegistryEntry(\n        identifier=\"id0012\",\n        name=[{\"@en\": \"sops encrypted secrets file\"}],\n        markers=[\n            {\"KEY\": \"sops\", \"EXISTS\": None},\n            {\"GOTO\": \"sops\", \"KEY\": \"kms\", \"EXISTS\": None},\n            {\"GOTO\": \"sops\", \"KEY\": \"pgp\", \"EXISTS\": None},\n        ],\n    ),\n```\n\n> **Pseudo code**:\nTest for the existence of keys `sops` in the primary JSON object.\n>\n> Goto the `sops` key and test for the existence of keys: `kms` and `pgp`\nwithin the `sops` object/value.\n\n### Local rules\n\nThe plan is to allow local rules to be run alongside the global ruleset. I\nexpect this will be a bit further down the line when the ruleset and\nmetaddata is more stabilised.\n\n## PRONOM\n\nIdeally `jsonid` can generate evidence enough to warrant the creration of\nPRONOM IDs that can then be referenced in the `jsonid` output.\n\nEvantually, PRONOM or a PRONOM-like tool might host an authoritative version\nof the `jsonid` registry.\n\n## Output format\n\nFor ease of development, the utility currently outputs `yaml`. The structure\nis still very fluid, and will also vary depending on the desired level of\ndetail in the registry, e.g. there isn't currently a lot of information about\nthe contents beyond a basic title and identifier.\n\nE.g.:\n\n```yaml\n---\njsonid: 0.0.0\nscandate: 2025-04-21T18:40:48Z\n---\nfile: integration_files/plain.json\nadditional:\n- '@en': data is dict type\ndepth: 1\ndocumentation:\n- archive_team: http://fileformats.archiveteam.org/wiki/JSON\nidentifiers:\n- rfc: https://datatracker.ietf.org/doc/html/rfc8259\n- pronom: http://www.nationalarchives.gov.uk/PRONOM/fmt/817\n- loc: https://www.loc.gov/preservation/digital/formats/fdd/fdd000381.shtml\n- wikidata: https://www.wikidata.org/entity/Q2063\nmime:\n- application/json\nname:\n- '@en': JavaScript Object Notation (JSON)\n---\n```\n\nThe structure should become more concrete as `jsonid` is formalized.\n\n## Analysis\n\n`jsonid` provides an analysis mechanism to help developers of identifiers. It\nmight also help users talk about interesting properties about the objects\nbeing analysed, and provide consistent fingerprinting for data that has\ndifferent byte-alignment but is otherwise identical.\n\n> **NB.**: Comments on existing statistics or ideas for new ones are\nappreciated.\n\n### Example analysis\n\n```json\n{\n  \"content_length\": 329,\n  \"number_of_lines\": 32,\n  \"line_warning\": false,\n  \"top_level_keys_count\": 4,\n  \"top_level_keys\": [\n    \"key1\",\n    \"key2\",\n    \"key3\",\n    \"key4\"\n  ],\n  \"top_level_types\": [\n    \"list\",\n    \"map\",\n    \"list\",\n    \"list\"\n  ],\n  \"depth\": 8,\n  \"heterogeneous_list_types\": true,\n  \"fingerprint\": {\n    \"unf\": \"UNF:6:sAsKNmjOtnpJtXi3Q6jVrQ==\",\n    \"cid\": \"bafkreibho6naw5r7j23gxu6rzocrud4pc6fjsnteyjveirtnbs3uxemv2u\"\n  },\n  \"encoding\": \"UTF-8\"\n}\n```\n\n## Utils\n\n### json2json\n\nUTF-16 can be difficult to read as UTF-16 uses two bytes per every one, e.g.\n`..{.\".a.\".:. .\".b.\".}.` is simply `{\"a\": \"b\"}`. The utility `json2json.py`\nin the utils folder will output UTF-16 as UTF-8 so that signatures can be\nmore easily derived. A signature derived for UTF-16 looks exactly the same\nas UTF-8.\n\n`json2json` can be called from the command line when installed via pip, or\nfind it in [src.utils][json2json-1].\n\n[json2json-1]: src/utils/json2json.py\n\n## Docs\n\nDev docs are [available][dev-docs-1].\n\n[dev-docs-1]: https://ffdev-info.github.io/jsonid/jsonid/\n\n----\n\n## Developer install\n\n### pip\n\nSetup a virtual environment `venv` and install the local development\nrequirements as follows:\n\n```bash\npython3 -m venv venv\nsource venv/bin/activate\npython -m pip install -r requirements/local.txt\n```\n\n### tox\n\n#### Run tests (all)\n\n```bash\npython -m tox\n```\n\n#### Run tests-only\n\n```bash\npython -m tox -e py3\n```\n\n#### Run linting-only\n\n```bash\npython -m tox -e linting\n```\n\n### pre-commit\n\nPre-commit can be used to provide more feedback before committing code. This\nreduces reduces the number of commits you might want to make when working on\ncode, it's also an alternative to running tox manually.\n\nTo set up pre-commit, providing `pip install` has been run above:\n\n* `pre-commit install`\n\nThis repository contains a default number of pre-commit hooks, but there may\nbe others suited to different projects. A list of other pre-commit hooks can be\nfound [here][pre-commit-1].\n\n[pre-commit-1]: https://pre-commit.com/hooks.html\n\n## Packaging\n\nThe [`justfile`][just-1] contains helper functions for packaging and release.\nRun `just help` for more information.\n\n[just-1]: https://github.com/casey/just\n\n### pyproject.toml\n\nPackaging consumes the metadata in `pyproject.toml` which helps to describe\nthe project on the official [pypi.org][pypi-2] repository. Have a look at the\ndocumentation and comments there to help you create a suitably descriptive\nmetadata file.\n\n### Versioning\n\nVersioning in Python can be hit and miss. You can label versions for\nyourself, but to make it reliaable, as well as meaningful is should be\ncontrolled by your source control system. We assume git, and versions can\nbe created by tagging your work and pushing the tag to your git repository,\ne.g. to create a release candidate for version 1.0.0:\n\n```sh\ngit tag -a 1.0.0-rc.1 -m \"release candidate for 1.0.0\"\ngit push origin 1.0.0-rc.1\n```\n\nWhen you build, a package will be created with the correct version:\n\n```sh\njust package-source\n### build process here ###\nSuccessfully built python_repo_jsonid-1.0.0rc1.tar.gz and python_repo_jsonid-1.0.0rc1-py3-none-any.whl\n```\n\n### Local packaging\n\nTo create a python wheel for testing locally, or distributing to colleagues\nrun:\n\n* `just package-source`\n\nA `tar` and `whl` file will be stored in a `dist/` directory. The `whl` file\ncan be installed as follows:\n\n* `pip install <your-package>.whl`\n\n### Publishing\n\nPublishing for public use can be achieved with:\n\n* `just package-upload-test` or `just package-upload`\n\n`just-package-upload-test` will upload the package to [test.pypi.org][pypi-1]\nwhich provides a way to look at package metadata and documentation and ensure\nthat it is correct before uploading to the official [pypi.org][pypi-2]\nrepository using `just package-upload`.\n\n[pypi-1]: https://test.pypi.org\n[pypi-2]: https://pypi.org\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "jsonid a json identification tool",
    "version": "0.8.1",
    "project_urls": {
        "Bug Reports": "https://github.com/ffdev-info/jsonid/issues/",
        "Homepage": "https://github.com/ffdev-info/jsonid",
        "Source": "https://github.com/ffdev-info/jsonid/sourcecode/"
    },
    "split_keywords": [
        "json",
        " digipres",
        " file-formats"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4ae8661ad5073c2880badad03346fffa79e06b33484782e77c6b3934250d26e6",
                "md5": "9cc796d8a1a5bb1dca4625d54a60ac17",
                "sha256": "544652ebe957446c0948eabfeb4145700fe3a940be1cabbf50b77c6147d9adde"
            },
            "downloads": -1,
            "filename": "jsonid-0.8.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9cc796d8a1a5bb1dca4625d54a60ac17",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 37012,
            "upload_time": "2025-07-15T18:21:25",
            "upload_time_iso_8601": "2025-07-15T18:21:25.998202Z",
            "url": "https://files.pythonhosted.org/packages/4a/e8/661ad5073c2880badad03346fffa79e06b33484782e77c6b3934250d26e6/jsonid-0.8.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "5268ce4c3e707834e78848ac8c9e72aff99b09ab9eeb3f4b148096dcf594f658",
                "md5": "0de3b430fb48b0bad8064f65a1cb4ca7",
                "sha256": "2cb8445d15b7e846af3815249e73ca31ea57576f8a6fe2fe89ac827c259961e0"
            },
            "downloads": -1,
            "filename": "jsonid-0.8.1.tar.gz",
            "has_sig": false,
            "md5_digest": "0de3b430fb48b0bad8064f65a1cb4ca7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 268633,
            "upload_time": "2025-07-15T18:21:28",
            "upload_time_iso_8601": "2025-07-15T18:21:28.437923Z",
            "url": "https://files.pythonhosted.org/packages/52/68/ce4c3e707834e78848ac8c9e72aff99b09ab9eeb3f4b148096dcf594f658/jsonid-0.8.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-15 18:21:28",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ffdev-info",
    "github_project": "jsonid",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "jsonid"
}
        
Elapsed time: 0.83534s