{"id":15012935,"url":"https://github.com/bwallrich/stricto","last_synced_at":"2026-04-08T14:05:41.572Z","repository":{"id":220162152,"uuid":"750819470","full_name":"bwallrich/stricto","owner":"bwallrich","description":"python structures with strict schema verification like jsonschema, directly in operators.","archived":false,"fork":false,"pushed_at":"2026-03-27T08:58:30.000Z","size":174,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-27T20:05:41.440Z","etag":null,"topics":["dict","json","json-schema","strict","strict-types","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bwallrich.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-01-31T11:38:03.000Z","updated_at":"2026-03-27T08:58:02.000Z","dependencies_parsed_at":"2024-02-08T22:47:12.217Z","dependency_job_id":"c40008fc-5187-4d94-91cf-27652155ffe7","html_url":"https://github.com/bwallrich/stricto","commit_stats":{"total_commits":54,"total_committers":1,"mean_commits":54.0,"dds":0.0,"last_synced_commit":"8f4b771263707cffcdf1994f2463ad0cb0aa85f7"},"previous_names":["bwallrich/stricto"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/bwallrich/stricto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwallrich%2Fstricto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwallrich%2Fstricto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwallrich%2Fstricto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwallrich%2Fstricto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bwallrich","download_url":"https://codeload.github.com/bwallrich/stricto/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwallrich%2Fstricto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31558408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dict","json","json-schema","strict","strict-types","validation"],"created_at":"2024-09-24T19:43:26.043Z","updated_at":"2026-04-08T14:05:41.566Z","avatar_url":"https://github.com/bwallrich.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stricto\n\n![release](https://img.shields.io/github/v/release/bwallrich/stricto.svg?label=latest)\n\n\n![pylint](https://img.shields.io/github/actions/workflow/status/bwallrich/stricto/pylint.yml?label=linter) ![test](https://img.shields.io/github/actions/workflow/status/bwallrich/stricto/test.yml?label=test)\n\nStrict json structure with schema validation, conditional sub structures, rights and a lot of features.\n\nThe way to use is very simple, see [Quickstart](#quickstart) for a basic setup.\n\nThe main difference with [jsonschema](https://github.com/python-jsonschema/jsonschema) is that the schema is directly in types of data. You don't have to *validate* them.\n\nSee [Documentation](https://stricto.readthedocs.io/en/latest/) for all details\n\n## Installation\n\n```bash\npip install stricto\n```\n\n## Quickstart\n\n```python\nfrom stricto import Dict, Int, String, List\n\na=Dict({\n    \"name\" : String(),\n    \"address\" : Dict({\n        \"num\" : Int(),\n        \"street\" : String()\n    }),\n    \"nicknames\" : List( String() )\n})\n\n\na.set({ \n    \"name\" : \"Edward\",\n    \"address\" : {\n        \"num\" : 22, \n        \"street\" : \"acacia avenue\"\n    },\n    \"nicknames\" : [ \"Ed\" ]\n})\n\n\nprint(a.address.num) # 22\nprint(a.address) # { \"num\" : 22, \"street\" : \"acacia avenue\" }\n\na.name = 666 # -\u003e raise a typeError (must be a string)\n\nprint (a) # { \"name\" : \"Edward\", ... }\n\na.nicknames.append(666) # -\u003e raise a typeError (must be a string)\na.nicknames.append(\"Eddy\")\na.nickname[1] # -\u003e Eddy\n\nb=a # b is a reference on a\nc=a.copy() # c is a different object : c is a copy\n\nc == b # return True (you can test and do operators directly on objects)\nb.nicknames.pop()\nc == b # return False\n```\n\n## Basic types\n\nAll Python basic classes are implemented in ```stricto```.\n\n| python class | type in stricto |\n| - | - |\n| bool | Bool() |\n| int | [Int()](#int) |\n| float | Float() |\n| string | [String()](#string) |\n| list | [List()](#list) |\n| dict | Dict() |\n| tuple | [Tuple()](#tuple) |\n| bytes | Bytes() |\n| | [In()](#in) |\n| datetime | Datetime() |\n\n### Example\n```python\nfrom stricto import Dict, Int\n\na = Int()\na.set(22) # -\u003e ok\na.set(23.1) # raise an error\na.set(\"the number of the beast\") # raise an error\n\n# WARNING\na = \"the number of the beast\" # works ! the affectation of \"a\" change. Now it is a string. This is python.\n\n# Inside a Dict().\ntest=Dict({\n    \"a\" : Int()\n})\n\ntest.a = 22 # -\u003e ok\ntest.a = 23.1 # raise an error\ntest.a = \"the number of the beast\" # raise an error\n```\n\n## JSON\n```python\n# example\nfrom stricto import Int, List, String, Dict, Error, StrictoEncoder\nimport json\n\nmodel={\n    \"b\" : Int(),\n    \"e\" : List( String())\n}\na=Dict(model)\nb=Dict(model)\na.set({ \"b\" : 1, \"e\" : [ \"aa\", \"bb\"]})\n\nsa = json.dumps(a, cls=StrictoEncoder) # json dumps. Need to user StrictoEncoder for specific types (see extend)\nb.set( json.loads(sa) ) \nb == a # return True\n```\n\n## Types\n### Common options\n\nCommonly available options for any type are:\n\n| Option | Default | Description |\n| - | - | - |\n| ```required=True\\|False``` | False | Check whether the field must have a value. |\n| ```description=\"whatever you want\"``` | None | Set a description for the item |\n| ```default=666``` | None | set a default value |\n| ```in=[ 1, 2, 3, 5 ]\\|func``` | None | Value must be in the list or pass the function check. |\n| ```union=[ 1, 2, 3, 5 ]\\|func``` | None | Alias for ```in```  |\n| ```transform=func``` | None | Set a [function](#functions) that [transforms](#transform) the value before assignment. |\n| ```constraint=func``` | None | Set a function to [check](#constraints) the value before asssignment. |\n| ```constraints=[func]``` | None | Same as above but for a list of [constraints](#constraints) |\n| ```onchange=func``` | None | set [onchange](#onchange) a function that will be trigged when the value change. |\n| ```onChange=func``` | None | Alias for ```onchange``` |\n| ```set=func``` | None | For read-only items ; set a function that will compute the value from others attributes. See [set or compute function](#set) |\n| ```compute=func``` | None | Alias for ```set``` |\n| ```exists=func``` | True | Set a function to check whether the object exists based on values from other attributes. See  [exists](#exists) for details |\n| ```can_read=func``` | True | Set a function to check whether the object is readable. see  [can_read](#can_read) for details |\n| ```can_modify=func``` | True | Set a function to check whether the object is changeable (read-only value). see  [can_modify](#can_modify) for details |\n| ```on=(event_name, function)``` | None | Register a function to be trigged when the specified event occurs. see  [events](#events) for details |\n| ```views=[ \"view1\", \"!view2\" ]``` | [] | Define the views this attribute is included or excluded from.. see  [views](#views) for details |\n\nSee [functions](#functions) for mor details and examples how to use them.\n\n### Int()\n```Int( options )``` maps the Python built-in `int` type.\n\nIn addition to the [generic options](#types), it supports specific options:\n\n| Option | Default | Description |\n| - | - | - |\n| ```min=``` | None | Minimum value |\n| ```minimum=21``` | None | Alias for  ```min``` |\n| ```max=99``` | None | Maximum value |\n| ```maximum=99``` | None | Alias for ```max=99``` |\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\nclient = Dict{\n    \"age\" : Int( min=21, max=120)\n}\n\nclient.age = 12  # -\u003e raise an error\nclient.age = 120  # -\u003e Ok\n\nnewAge = client.age+1 # -\u003e raise an Error ( \u003e max ) newAge is implicitly an Int( min=21, max=120))\nnewAge = 1+client.age # -\u003e Ok (newAge is implicitly an int)\n```\n\n### String()\n\n```String( options )``` maps the Python built-in `str` string type.\n\nIn addition to the [generic options](#types), it supports specific options:\n\n| Option | Default | Description |\n| - | - | - |\n| ```pattern=regexp``` | None | Must match this `regexp` |\n| ```patterns=[reg1, reg2]``` | None | Must match all the regular expressions in the list |\n| ```regexp=``` | None | Alias for ```pattern``` |\n\n#### Example\n```python\na=String( pattern='^A' )\na.set('Foo')        # -\u003e raise an error\na.set('AZERTY')     # OK\n\n# list of regexp\na=String( patterns=[ '^A', r'.*Z$' ] )\na.set('Allo')        # -\u003e raise an error\na.set('AtoZ')        # OK\n\n# function return a regexp\na=String( pattern=lambda self, value, root : r'.*Z$')\na.set('Allo')        # -\u003e raise an error\na.set('AtoZ')        # OK\n\n```\n\n### List()\n```List( options )``` maps the Python built-in `list` type.\n\nIn addition to the [generic options](#types), it supports specific options:\n\n| Option | Default | Description |\n| - | - | - |\n| ```min=``` | None | Minimum number of elements in the list |\n| ```minimum=21``` | None | Alias for  ```min``` |\n| ```max=99``` | None | Maximum number of elements in the list |\n| ```maximum=99``` | None | Alias for `max` |\n| ```uniq=True``` | None | Forbid duplicate values |\n\n#### Example\n```python\n# example\nfrom stricto import Dict, List\n\nclient = Dict{\n    \"nicknames\" : List( String(), default=[], uniq=True, min=0, max=3)\n}\n\nclient.nicknames = [ \"Ed\", \"Eddy\", \"Edward\" ]  # -\u003e raise an error\nclient.nicknames = [ \"Ed\" ]  # -\u003e Ok\nclient.nicknames.append( \"Ed\" ) # -\u003e raise an error (must be uniq)\n```\n\n### Tuple()\n```Tuple( options )``` maps the Python built-in `tuple` type.\n\nIt supports all the [generic options](#types).\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Tuple\n\nclient = Dict{\n    \"address\" : Tuple( (Int(), String()) )\n}\n\nprint(client.address) # -\u003e None\nclient.address = ( 12, \"accacia avenue\" )  # -\u003e Ok\nclient.address[1] # -\u003e \"acacia avenue\"\nclient.address[0] = 13  # -\u003e raise an error like a standard tuple\nclient.address = ( 13, \"accacia avenue\" )  # -\u003e Ok\n```\n\n### In()\n```In( [ Array of types ] )``` builds the union of several types ; in other words, it allows the attribute to be set with values of different types.\n\nIt supports all the [generic options](#types).\n\n#### Example\n```python\n# example\nfrom stricto import In, Int, String\n\na = In( [ Int(), String() ] )\n\na.set(\"hello\") # -\u003e OK\na.count('h') # -\u003e return 1\n\na.set(12) # -\u003e OK\na.bit_length() # -\u003e return 4\na.count('h') # -\u003e return None\n\na.set(3.14) # -\u003e raise an error\n```\n\n## Functions\n\nA `func` can return a value to modify the result. It can also be a lambda.\n\n### transform\n\nPlease refer to [transform function](#types)\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\ndef upper(value, o):\n    \"\"\"\n    transform the value into upper\n\n    value : the current value given (\"worldcompagny\" in this example).\n    o     : the full object\n    \"\"\"\n    return value.upper()\n\ncompany=Dict({\n    \"name\" : String( transform=upper ),\n})\n\ncompany.name=\"worldcompagny\"\nprint(company.name) # -\u003e \"WORLDCOMPAGNY\"\n```\n\n### set\n\nIt allows to define a function that will compute the field's value.\n\nThis computed field is read-only.\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\na=Dict({\n    \"b\" : Int( default = 0, set=lambda o: o.c+1 ),\n    \"d\" : Int( default = 0, set=lambda o: o.b+1 ),\n    \"c\" : Int( ),\n})\n\n# \"b\" and \"d\" cannot be modified by hand. the are recalculated every time another value \n# change in the Dict.\n\na.b = 3 # -\u003e raise an error\n\na.c = 2\nprint(a.b) # -\u003e 3\nprint(a.d) # -\u003e 4\n```\n\n### constraints\nIt allows to define a function that checks whether the constraint on the attribute's value is respected.\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\n\ndef check_pair(value, o): # pylint: disable=unused-argument\n    \"\"\"\n    return true if pair\n    \"\"\"\n    return not value % 2\n\na=Dict({\n    \"b\" : Int( default = 0, constraint=check_pair ),        # check before setting\n    \"d\" : Int( constraint=lambda value, o : not value % 2 ), # same as above, with a lambda\n    \"c\" : Int( constraints=[ check_pair ] ),                # A list of constraints\n})\n\na.b = 2 # OK\na.c = 3 # -\u003e raise an error\n```\n\n### onchange\nIt allows to define a listener function about the attribute value change event.\n\n#### Example\n```python\nfrom stricto import Dict, Int, String\n\ndef change_test(old_value, value, o): # pylint: disable=unused-argument\n    \"\"\"\n    just a change option\n    old_value   -\u003e The previous value\n    value       -\u003e the new one\n    o           -\u003e the root object = a in our example\n    \"\"\"\n    print(f\"The value of b as changed from {old_value} to {value}\")\n\na=Dict({\n    \"b\" : Int( default = 0, onchange=change_test )\n})\n\na.b = 2     # -\u003e output \"The value of b as changed from 0 to 2\"\na.b = 3-1   # -\u003e nothing displayed\n```\n\n### exists\nIt allows to define a function that checks the attribute's existence.\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\ndef check_if_female(value, o):\n    \"\"\"\n    return true if Female\n    \"\"\"\n    if o.gender == \"Male\":\n        return False\n    return True\n\ncat=Dict({\n    \"name\" : String(),\n    \"gender\" : String( default = 'Male', in=[ 'Male', 'Female' ]),\n    \"female_infos\" : Dict(\n        {\n        \"number_of_litter\" : Int(default=0, required=True)\n        # ... some other attributes\n\n    }, exists=check_if_female )\n})\n\ncat.set({ \"name\" : \"Felix\", \"gender\" : \"Male\" }\ncat.female_infos   # -\u003e None\ncat.female_infos.number_of_litter = 2 # -\u003e Raise an Error\n\ncat.gender = \"Female\"\ncat.female_infos.number_of_litter = 2 # -\u003e Ok\ncat.female_infos # -\u003e { \"number_of_litter\" : 2 }\n```\n\n\n## Permissions\nEach attribute can have its own set of permissions.\nA Permission is materialized by a ```can_\u003cpermission\u003e``` parameter for the attribute.\n\nCurrently 2 permissions are defined:\n\n| Permission | description |\n| - | - |\n| can_read | read : The ability to read the attribute's value |\n| can_modify | modify : The ability to modify the attribute |\n\nThe developer is free to add any permission he needs.\n\n### can_read\n\nIt allows to define a function that checks whether the attribute is readable ; returns a boolean.\n\nIt shall not be confused with [exists](#exists) : an attribute can exist but not be readable for such user.\n\n#### Example\n```python\n# example\nfrom stricto import Dict, Int, String\n\ncurrent_user_name=\"John\"\n\ndef can_see_and_modify_salary(value, o):\n\n    \"\"\"\n    return true if can read the salary\n    \"\"\"\n    global current_user_name\n    if current_user_name == o.name:\n        return True\n    return False\n\n\nuser=Dict({\n    \"name\" : String(),\n    \"salary\" : Int( default=0, can_read=can_see_and_modify_salary, can_modify=can_see_and_modify_salary ),\n})\n\nuser.set({ \"name\" : \"John\", \"salary\" : 20000 }\nuser.salary   # -\u003e 20000\n\nuser.name=\"Jack\"\nuser.salary # -\u003e raise an error\n```\n\n\n## selectors\n\nYou can use json selectors to find the object according to [rfc9535](https://datatracker.ietf.org/doc/rfc9535/)\n\n### select, multi_select\n\nYou can use select() for a single selection or multi_select() for a list of selections\n\n```python\nfrom stricto import Int, List, String, Dict, Error\n\na = Dict(\n    {\n        \"a\": Int(default=1),\n        \"b\": Dict({\n            \"l\" : List( Dict({\n                \"i\" : String()\n            }) )\n        }),\n        \"c\": Tuple( (Int(), String()) )\n    }\n)\na.set({ \"a\" : 12, \"b\" : { \"l\" : [ { \"i\" : \"fir\"}, { \"i\" : \"sec\"}, ] }, \"c\" : ( 22, \"h\") })\n\na.select('$.a') # 12\n\n# To make the difference :\n\na.select('$.f.d') # None\na.f.d # -\u003e raise an error\n\na.select(\"$.b.l[0].i\") # \"fir\"\na.select(\"$.*.l.i\") # [\"fir\", \"sec\"]\na.select(\"$.*.l[0:2].i\") # [\"fir\", \"sec\"]\n\n# multi_select\na.multi_select( [ \"$.a\", \"$.c\" ] ) # [ 12 , ( 22, \"h\") ]\n\n```\n\n\n## Matching\n\nYou can match an object with some kind of filters. \n\nMatching is done with ```match( dict )``` method.\n\nAvailable operators are :\n\n| operator | syntax | example | description |\n| - | - | - | - |\n| $and | ( \"$and\", [ condition, condition ] ) | ( \"\\$and\", [ ( \"\\$gt\", 1 ), ( \"\\$lt\" : 2 )]) | Do an *and* on conditions |\n| $or | ( \"$or\", [ condition, condition ] ) |  ( \"\\$or\", [ ( \"\\$gt\", 10 ), ( \"$eq\" : 0 )]) | Do an *or* on conditions |\n| $eq | ( \"$eq\", value ) |  ( \"\\$eq\", \"toto\" ) | Equality |\n| $ne | ( \"$ne\", value ) |  ( \"\\$ne\", \"toto\" ) | Not equal |\n| $lt | ( \"$lt\", value ) |  ( \"\\$lt\", 1 ) | Less than |\n| $lte | ( \"$lte\", value ) |  ( \"\\$lte\", 1 ) | Less than or equal |\n| $gt | ( \"$gt\", value ) |  ( \"\\$gt\", 1 ) | Greater than |\n| $gte | ( \"$gte\", value ) |  ( \"\\$gte\", 1 ) | Greater than or equal |\n| $not | ( \"$not\", condition ) |  ( \"\\$not\", ... ) | Not |\n| $reg | ( \"$reg\", regexp ) |  ( \"\\$reg\", r'Jo' ) | A regular expression; match only on strings (match \"start with Jo\" in this example.) |\n| $contains | ( \"$contains\", condition ) |  ( \"\\$contains\", ( \"$reg\", r'^Jo' ) ) | a list contains one or more elements matching the condition |\n\n\n\n### Example\n\n```python\nfrom stricto import Int, List, String, Dict, Error\n\na = Dict(\n    {\n        \"name\"    : String()\n        \"surname\" : String()\n        \"incomes\" : Dict({\n                \"salary\" : Int(),\n                \"royalties\" : Int(),\n                \n        }),\n    }\n)\n\na.set( { \"name\" : \"John\", \"surname\" : \"Doe\", \"incomes\" : { \"salary\" : 50000 }})\n\n# Match with equality \na.match( { \"surname\" : \"Doe\" } ) -\u003e return True\na.match( { \"incomes\" : { \"salary\" : 20000 } } ) -\u003e return False\n\n# Match with operators\na.match( { \"incomes\" : { \"salary\" : ( \"$gt\", 20000 ) } } ) -\u003e return True\n\n# Match with $or\na.match( ( \"$or\", [ ( \"surname\", \"Doe\" ), ( \"salary\" : ( \"$gt\", 60000 ) ) ]) ) -\u003e return True\n```\n\n## patch\n\nYou can patch object in the sense of https://datatracker.ietf.org/doc/html/rfc6902, but with a merge of [selectors]](#selectors).\n\n### Example\n```python\nfrom stricto import Int, List, String, Dict, Error\n\na = Dict(\n    {\n        \"name\"    : String()\n        \"surname\" : String()\n        \"incomes\" : Dict({\n                \"salary\" : Int(),\n                \"royalties\" : Int(),\n                \n        }),\n    }\n)\n\na.set( { \"name\" : \"John\", \"surname\" : \"Doe\", \"incomes\" : { \"salary\" : 50000 }})\n\na.patch( 'replace', '$.name', \"Jenny\" )\n# equivalent of a.name = Jenny\n\n```\n\n## Events\n\nA stricto object can be trigged by custom events.\n\n### Example\n```python\nimport random\nfrom stricto import Dict, Int, String\n\ndef random( event_name, root, me ):\n    me.set(random.randint(1, 6))\n\n\nuser=Dict({\n    \"name\" : String(),\n    \"dice1\" : Int( default=1, on=('roll' , random) ),\n    \"dice2\" : Int( default=1, on=[ ('roll' , random)] ),\n})\n\n\nuser.set({ \"name\" : \"dice1and2\" })\n# Later\nuser.trigg('roll')\nuser.dice1 # -\u003e A number 1-6\nuser.dice2 # -\u003e A number 1-6\n```\n\n## Views\n\n```Views``` allows to define partial versions of a Stricto Item, i.e. versions that include only a subset of the attributes. \n\nYou can specify in views :\n\n* Belong *explicitely* to a view with ```views=[ \"my_view\" ]```\n* Belong *explicitely* not to be in a view with ```views=[ \"!my_view\" ]```\n\nYou can specify in ```get_view()``` :\n\n* an view with all fields excepts those explixitely marked with a \"!\"with ```get_view(\"my_view\")```\n* an explicite view (only those explicitely marked in view) with ```get_view(\"+my_view\")```\n\n### summary\n\n| call\\set | test | !test | none |\n| -- | -- | -- | -- |\n| none | X | X | X |\n| test | X | | X |\n| +test | X | | |\n\n\n\n\n### Example\n```python\nfrom stricto import Dict, Int, String\n\n# ISO 3166 country reference\ncountry=Dict({\n    \"name\" : String( view=[ \"short\" ] ),\n    \"a2\" : String( view=[ \"short\" ] ),\n    \"a3\" : String(),\n    \"num\" : String(),\n    \"flag_url\" : String( set=lambda o: f\"https://flagcdn.com/256x192/{o.a2}.png\", view=[\"!save\", \"short\" ] ),\n})\n\ncountry.set({ \"name\" : \"Ukraine\", \"a2\" : \"UA\", a3 : \"UKR\", \"num\" : \"804\" })\n\n# Whant only fields explicitely in view \"short\"\nv = country.get_view(\"+short\")\n# type(v) is a Dict\n# v = { \"name\" : \"Ukraine\", \"a2\" : \"UA\", \"flag_url\" : \"https://flagcdn.com/256x192/UA.png\" }\n\n# Whant all fields excepts those with \"!short\". so all\nl = country.get_view(\"short\")\n# l == country\n\ns = country.get_view(\"save\")\n# type(s) is a Dict\n# s = { \"name\" : \"Ukraine\", \"a2\" : \"UA\", a3 : \"UKR\", \"num\" : \"804\" }\nl = country.get_view(\"+save\")\n# l == None\n\nl = country.get_view(\"blabla\")\n# l == country\nl = country.get_view(\"+blabla\")\n# l == None\n```\n\n## Schemas\n\nYou can extract a schema as a ```dict```.\n\n\n### Example\n```python\nimport stricto\n\ndef check_pair():\n    pass\n\na = Dict(\n    {\n        \"b\": List(String()),\n        \"c\": In([String(), Int( constraint=check_pair )]),\n        \"d\": Tuple([String(require=True), Bool()]),\n    }\n)\nb = Dict(\n    {\n        \"b\": List(String()),\n        \"c\": In([String(), Int( constraints=[check_pair])]),\n        \"d\": Tuple([String(), Bool()]),\n    }\n)\n\na.get_schema() == b.get_schema() # False, a.d and b.d differs.\n```\n\nYou can also extend an existing schema :\n\n```python\n# Adding a new 'key' in the previous schema\na.add_to_model(\n            \"e\", String()\n        )\na.e='it works !'\n# now a.e exists\n\na.remove_model( 'e' )\na.e # raise an error.\n```\n\n\n## Extended types\nAn extented type is a type that inherits some of its properties from a *parent* type.\n\n### Using Extend\n\nYou can define your own *stricto compatible type* using ```Extend```.\n\nFor that, you have to extend your type from ```Extend```, and define methods for encoding and decoding the object.\n\nYou have to the ```__repr__``` funtion too.\n\n\n#### Example\nIn the example below we define a *Datetime* type, which extends the *datetime* Python type.\n\n```python\nfrom datetime import datetime\nfrom stricto import Extend\n\n\nclass Datetime(Extend):\n    \"\"\"\n    A specific class to play with datetime\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"\n        initialisation. Must pass the type (datetime) in args for Extend\n        \"\"\"\n        super().__init__(datetime, **kwargs)\n\n    def __json_encode__(self):\n        \"\"\"\n        Called by the specific Encoder\n        to encode datetime\n        \"\"\"\n        return self.get_value().isoformat()\n\n    def __json_decode__(self, value):\n        \"\"\"\n        Called by the specific Decoder\n        to decode a datetime\n        \"\"\"\n        return self._type.fromisoformat(value)\n\n\na=Datetime()\na.set(datetime(2000, 1, 1))\na.year # 2000 \n```\n\n### Using Dict\n\nBy this way, you have the possibility to define the type's custom structure.\n\n#### Example\n```python\nfrom stricto import Dict, Float\n\nclass Complex(Dict):\n    \"\"\"\n    A specific class to play with Dict\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"\n        initialisation. Must define the struct\n        \"\"\"\n        super().__init__(\n            {\n            \"real\": Float(), \n            \"imag\": Float()\n            },\n            **kwargs)\n\n    def __repr__(self):\n        return f\"({self.real}+{self.imag}i)\"\n\n    def __add__(self, other):\n        \"\"\"\n        add two complex\n        \"\"\"\n        if not isinstance(other, Complex):\n            raise TypeError(\"can only add Complex\")\n\n        r = self.__copy__()\n        r.real = self.real + other.real\n        r.imag = self.imag + other.imag\n        return r\n\na = Dict({\"b\": Complex(), \"c\": Int(default=0)})\na.b.real = 12.0\na.b.imag = 9.0\nself.assertEqual(repr(a.b), \"(12.0+9.0i)\")\n```\n\n## Tests \u0026 co\n\nFor personal use only\n\n```bash\n# all tests\npython -m unittest tests\n# or for only some tests\npython -m unittest tests/test_bool.py\n# or for a specific test\npython -m unittest tests.TestDict.test_simple_type\n\n# reformat\npython -m black .\n\n# pylint\npylint $(git ls-files '*.py')\n\n# coverage\ncoverage run -m unittest tests\ncoverage html # report under htmlcov/index.html\nfirefox htmlcov/index.html\n```\n\n### Building a new release\n\nFor personal use only\n\n```bash\n# Modify changelog\n# modify pyproject.toml\ngit add -u\ngit commit -am 'preparing 0.0.x'\ngit push\ngit tag -a 0.0.x -m '0.0.x'\ngit push origin tag 0.0.x\n\n# publish a new relase in github interface, based on tag \n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwallrich%2Fstricto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbwallrich%2Fstricto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwallrich%2Fstricto/lists"}