# core


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

We begin with some helper functions

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L61"
target="_blank" style="float:right; font-size:smaller">source</a>

### hash_json

>  hash_json (data:Any)

*Compute a SHA-256 hash of JSON-serializable data.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L52"
target="_blank" style="float:right; font-size:smaller">source</a>

### extend_path

>  extend_path (base:str, key_or_index:Union[str,int])

*Append a segment to a JSONPath using bracket notation.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L47"
target="_blank" style="float:right; font-size:smaller">source</a>

### path_for_key

>  path_for_key (key:str)

*Get a JSONPath for a given key using bracket notation.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L43"
target="_blank" style="float:right; font-size:smaller">source</a>

### sha256_hex

>  sha256_hex (data:bytes)

*Get the hex digest of the SHA-256 hash of the given data*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L16"
target="_blank" style="float:right; font-size:smaller">source</a>

### hex_to_bytes

>  hex_to_bytes (h:str)

\*Convert a hex string to raw bytes.

Accepts optional ‘0x’ prefix and common separators (space, underscore,
hyphen, colon). Raises TypeError for non-str inputs and ValueError for
invalid hex or odd length after cleaning.\*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L72"
target="_blank" style="float:right; font-size:smaller">source</a>

### OperationHeader

>  OperationHeader (kind:str, task:str, tool:str, output_type:str,
>                       event_uuid:Optional[bytes], timestamp:Optional[str],
>                       meta:Any)

*Metadata about an operation.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L436"
target="_blank" style="float:right; font-size:smaller">source</a>

### Scrapebook

>  Scrapebook ()

*An ABC exposing the basic API for Scrapebook*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L351"
target="_blank" style="float:right; font-size:smaller">source</a>

### Recorder

>  Recorder (book:Scrapebook, kind:str, task:str, tool:str, output_type:str)

*Fixed (kind, task, tool, output_type).*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L313"
target="_blank" style="float:right; font-size:smaller">source</a>

### OperationResult

>  OperationResult (op:__main__.Operation, path:str)

*Handle: (Operation, JSONPath) into that operation’s decoded results.
Supports subscripting to extend the path: op\[“a”\]\[“b”\]\[0\]*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L142"
target="_blank" style="float:right; font-size:smaller">source</a>

### Operation

>  Operation (book:Scrapebook, op_id:bytes)

*Lazy header; decoded results/inputs are cached along with their
dependency sets.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L107"
target="_blank" style="float:right; font-size:smaller">source</a>

### Artifact

>  Artifact (book:Scrapebook, sha256:bytes)

*Lazy-loaded bytes referenced by SHA-256; caches after first load.*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L325"
target="_blank" style="float:right; font-size:smaller">source</a>

### OperationResult.value

>  OperationResult.value ()

*Evaluate JSONPath on the op’s **decoded cached** results. - 0 matches
-\> None - 1 match -\> the value - \>1 matches-\> list of values*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L248"
target="_blank" style="float:right; font-size:smaller">source</a>

### Operation.inputs

>  Operation.inputs ()

*Get an operation’s inputs*

------------------------------------------------------------------------

<a
href="https://github.com/imbrem/scrapebook/blob/main/scrapebook/core.py#L214"
target="_blank" style="float:right; font-size:smaller">source</a>

### Operation.results

>  Operation.results ()

*Get the results of an operation*

``` python
class ScrapebookDict(Scrapebook):
    def __init__(self):
        self.ops = dict()
        self.arts = dict()
        self.deps = dict()
        self.op_used_by = dict()
        self.produced_by = dict()
        self.consumed_by = dict()
        pass

    # persistence
    def persist_operation(
        self,
        *,
        header: OperationHeader,
        inputs_json: Any,
        results_json: Any,
        deps: Set[bytes],
        input_arts: Set[bytes],
        output_arts: Set[bytes]
    ) -> bytes:
        op_id = header.op_id(inputs_json, results_json)
        self.ops[op_id] = { "header" : header, "input" : inputs_json, "result" : results_json}
        for dep in deps:
            self.op_used_by.setdefault(dep, set()).add(op_id)
            self.deps.setdefault(op_id, set()).add(dep)
        for art in input_arts:
            self.consumed_by.setdefault(art, set()).add(op_id)
        for art in output_arts:
            self.produced_by.setdefault(art, set()).add(op_id)
        return op_id

    def fetch_operation_header(self, op_id: bytes) -> OperationHeader:
        return self.ops[op_id]["header"]

    def fetch_results_json(self, op_id: bytes) -> Any:
        return self.ops[op_id]["result"]
    
    def fetch_inputs_json(self, op_id: bytes) -> Any:
        return self.ops[op_id]["input"]

    def fetch_op_ids(self) -> Iterable[bytes]:
        return self.ops.keys()

    def put_artifact_sha256(self, data: bytes) -> bytes:
        """Store bytes content-addressed by SHA-256 and return the hex hash."""
        hash = hashlib.sha256(data).digest()
        self.arts[hash] = data
        return hash
    
    def fetch_artifact_bytes_sha256(self, sha256: bytes) -> bytes:
        return self.arts[sha256]
    
    def fetch_artifact_produced_by(self, sha256: bytes) -> Iterable[bytes]:
        """
        Given the hash of an artifact, return the set of operation IDs that produce it.
        """
        return self.produced_by.get(sha256, set())

    def fetch_artifact_consumed_by(self, sha256: bytes) -> Iterable[bytes]:
        """
        Given the hash of an artifact, return the set of operation IDs that consume it.
        """
        return self.consumed_by.get(sha256, set())
```

``` python
book = ScrapebookDict()
```

``` python
scrapes = book.obs_recorder(task="scrape_site", tool="demo", output_type="http_response")
```

``` python
op = scrapes.record({"url": "http://example.com"}, {"status": 200, "content": b"hello"})
op.validate()
```

``` python
book.fetch_artifact_produced_by(op.id)
```

    set()

``` python
(op, op.kind, op.task, op.tool, op.output_type, op.event_uuid.hex(), op.timestamp, op.meta)
```

    (<Operation ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784>,
     'obs',
     'scrape_site',
     'demo',
     'http_response',
     '0321dd21d2eabb8da0ea55383fabc053e4bccf7dc2f87c1de29404f0eee5b466',
     '2025-10-05T02:58:41.209504',
     None)

``` python
op.results_json()
```

    {'status': 200,
     'content': {'$artifact': '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'}}

``` python
op.results()
```

    {'status': 200,
     'content': <Artifact 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824>}

``` python
op["content"].value().bytes()
```

    b'hello'

``` python
op["content"]
```

    <OperationResult id=ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784 path='$["content"]'>

``` python
articles = book.trans_recorder(task="parse_article", tool="demo2", output_type="article")
```

``` python
op2 = articles.record({"content" : op["content"], "mode": "cool"}, "good article")
```

``` python
(
    op2, op2.kind, op2.task, op2.tool, op2.output_type, op2.event_uuid, 
    op2.timestamp, op2.meta
)
```

    (<Operation d37127eba9c227c45f73d26c34d4305f90ceff4a0f253577e809a32bbc243cc8>,
     'trans',
     'parse_article',
     'demo2',
     'article',
     None,
     None,
     None)

``` python
op2.value()
```

    'good article'

``` python
op2.inputs()
```

    {'content': <OperationResult id=ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784 path='$["content"]'>,
     'mode': 'cool'}

``` python
[dep for dep in op2.deps()]
```

    [<Operation ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784>]

``` python
op3 = articles.record(
    {"content" : op["content"], "mode": op2, "status": op["status"]}, 
    "better article"
)
```

``` python
op3
```

    <Operation f60c4785522a91181c5110336408465d2591b9f34a59b231ec2e5deea6b48377>

``` python
op3.value()
```

    'better article'

``` python
[dep for dep in op3.deps()]
```

    [<Operation ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784>,
     <Operation d37127eba9c227c45f73d26c34d4305f90ceff4a0f253577e809a32bbc243cc8>,
     <Operation ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784>]

``` python
op
```

    <Operation ae67f785a7e39b117161a13fdd2d6a5139f3e42d8bea6f9e3b264e853a45d784>

``` python
op.validate()
```

``` python
op.artifacts_produced()
```

    {<Artifact 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824>}

``` python
book.validate()
```

    3
