{"id":34061157,"url":"https://github.com/trevorpogue/sections","last_synced_at":"2026-04-02T02:09:18.652Z","repository":{"id":49184063,"uuid":"379734101","full_name":"trevorpogue/sections","owner":"trevorpogue","description":"Easy Python tree data structures","archived":false,"fork":false,"pushed_at":"2022-02-27T16:46:21.000Z","size":321,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-16T01:46:20.512Z","etag":null,"topics":["attributes","attrs","category","data","dict","graph","list","node","organize","python","section","structure","tree","vertex"],"latest_commit_sha":null,"homepage":"https://sections.readthedocs.io/","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/trevorpogue.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":"CONTRIBUTING.rst","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"trevorpogue","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2021-06-23T21:33:18.000Z","updated_at":"2024-11-28T16:35:01.000Z","dependencies_parsed_at":"2022-09-16T17:10:50.736Z","dependency_job_id":null,"html_url":"https://github.com/trevorpogue/sections","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/trevorpogue/sections","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorpogue%2Fsections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorpogue%2Fsections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorpogue%2Fsections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorpogue%2Fsections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trevorpogue","download_url":"https://codeload.github.com/trevorpogue/sections/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorpogue%2Fsections/sbom","scorecard":{"id":898302,"data":{"date":"2025-08-11","repo":{"name":"github.com/trevorpogue/sections","commit":"6457d3feaab8bb7bd07886bb024bf07408f8472c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2021-437 / GHSA-5xp3-jfq3-5q8x","Warn: Project is vulnerable to: PYSEC-2020-173 / GHSA-gpvv-69j7-gwj8","Warn: Project is vulnerable to: PYSEC-2023-228 / GHSA-mq26-g339-26xf","Warn: Project is vulnerable to: PYSEC-2025-49 / GHSA-5rjg-fvgr-3xxf","Warn: Project is vulnerable to: GHSA-cx63-2mw6-8hw5","Warn: Project is vulnerable to: PYSEC-2022-43012 / GHSA-r9hx-vwmv-q579","Warn: Project is vulnerable to: PYSEC-2024-187 / GHSA-rqc4-2hc7-8c8v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T14:34:10.916Z","repository_id":49184063,"created_at":"2025-08-24T14:34:10.916Z","updated_at":"2025-08-24T14:34:10.916Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["attributes","attrs","category","data","dict","graph","list","node","organize","python","section","structure","tree","vertex"],"created_at":"2025-12-14T04:40:57.166Z","updated_at":"2026-04-02T02:09:18.643Z","avatar_url":"https://github.com/trevorpogue.png","language":"Python","readme":".. start-badges\n\n.. |coveralls| image:: https://coveralls.io/repos/github/trevorpogue/sections/badge.svg\n    :alt: Coverage Status\n    :target: https://coveralls.io/github/trevorpogue/sections\n\n.. |codacy| image:: https://app.codacy.com/project/badge/Grade/92804e7a0df44f09b42bc6ee1664bc67\n    :alt: Codacy Code Quality Status\n    :target: https://www.codacy.com/gh/trevorpogue/sections/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=trevorpogue/sections\u0026amp;utm_campaign=Badge_Grade\n\n.. |codeclimate| image:: https://codeclimate.com/github/trevorpogue/sections/badges/gpa.svg\n   :alt: CodeClimate Quality Status\n   :target: https://codeclimate.com/github/trevorpogue/sections\n\n.. |version| image:: https://img.shields.io/pypi/v/sections.svg\n    :alt: PyPI Package latest release\n    :target: https://pypi.org/project/sections\n\n.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/sections.svg\n    :alt: Supported versions\n    :target: https://pypi.org/project/sections\n\n.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/sections.svg\n    :alt: Supported implementations\n    :target: https://pypi.org/project/sections\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/sections.svg\n    :alt: PyPI Wheel\n    :target: https://pypi.org/project/sections\n\n.. |downloads| image:: https://pepy.tech/badge/sections\n    :alt: downloads\n    :target: https://pepy.tech/project/sections\n\n.. |downloads-week| image:: https://pepy.tech/badge/sections/week\n    :alt: downloads\n    :target: https://pepy.tech/project/sections\n\n.. |docs| image:: https://readthedocs.org/projects/sections/badge/?style=flat\n    :alt: Documentation Status\n    :target: https://sections.readthedocs.io/\n\n.. |requires| image:: https://requires.io/github/trevorpogue/sections/requirements.svg?branch=main\n    :alt: Requirements Status\n    :target: https://requires.io/github/trevorpogue/sections/requirements/?branch=main\n\n.. |commits-since| image:: https://img.shields.io/github/commits-since/trevorpogue/sections/v0.0.3.svg\n    :alt: Commits since latest release\n    :target: https://github.com/trevorpogue/sections/compare/v0.0.3...main\n\n.. end-badges\n\n==============================\n[ s e | c t | i o | n s ]\n==============================\n\n|coveralls| |codacy| |codeclimate| |requires|\n\n|version| |supported-versions| |supported-implementations| |wheel|\n\n|docs| |commits-since| |downloads-week| |downloads|\n\nPython package providing flexible tree data structures for organizing lists and dicts into sections.\n\nSections is designed to be:\n\n* **Intuitive**: Start quickly, spend less time reading the docs.\n\n* **Scalable**: Grow arbitrarily complex trees as your problem scales.\n\n* **Flexible**: Rapidly build nodes with custom attributes, properties, and methods on the fly.\n\n* **Fast**: Made with performance in mind - access lists and sub-lists/dicts in Θ(1) time in many cases. See the Performance section for the full details.\n\n* **Reliable**: Contains an exhaustive test suite and 100% code coverage.\n\n----------------------------------------------------------------\nLinks\n----------------------------------------------------------------\n* `GitHub \u003chttps://github.com/trevorpogue/sections\u003e`_\n\n* `Documentation \u003chttps://sections.readthedocs.io\u003e`_\n\n=========================\nUsage\n=========================\n\n.. code-block:: bash\n\n   $ pip install sections\n\n.. code-block:: python\n\n   import sections\n\n   menu = sections(\n       'Breakfast', 'Dinner',\n       main=['Bacon\u0026Eggs', 'Burger'],\n       side=['HashBrown', 'Fries'],\n   )\n\n::\n\n   $ print(menu)\n    _________________________\n   │  _____________________  │\n   │ │ 'Breakfast'         │ │\n   │ │ main = 'Bacon\u0026Eggs' │ │\n   │ │ side = 'HashBrown'  │ │\n   │  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯  │\n   │  _________________      │\n   │ │ 'Dinner'        │     │\n   │ │ main = 'Burger' │     │\n   │ │ side = 'Fries'  │     │\n   │  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯      │\n    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\n\n.. code-block:: python\n\n   # menu's API with the expected results:\n   assert menu.mains == ['Bacon\u0026Eggs', 'Burger']\n   assert menu.sides == ['HashBrown', 'Fries']\n   assert menu['Breakfast'].main == 'Bacon\u0026Eggs'\n   assert menu['Breakfast'].side == 'HashBrown'\n   assert menu['Dinner'].main == 'Burger'\n   assert menu['Dinner'].side == 'Fries'\n   assert menu('sides', list) == ['HashBrown', 'Fries']\n   assert menu('sides', dict) == {'Breakfast': 'HashBrown', 'Dinner': 'Fries'}\n   # root section/node:\n   assert isinstance(menu, sections.Section)\n   # child sections/nodes:\n   assert isinstance(menu['Breakfast'], sections.Section)\n   assert isinstance(menu['Dinner'], sections.Section)\n\n**Scale in size:**\n\n.. code-block:: python\n\n   library = sections(\n       \"My Bookshelf\",\n       sections({'Fiction'},\n                'LOTR', 'Harry Potter',\n                author=['JRR Tolkien', 'JK Rowling'],\n                topic=[{'Fantasy'}, 'Hobbits', 'Wizards'],),\n       sections({'Non-Fiction'},\n                'General Relativity', 'A Brief History of Time',\n                author=['Albert Einstein', 'Steven Hawking'],\n                topic=[{'Physics'}, 'Time, Gravity', 'Black Holes'],\n                ),\n   )\n\n::\n\n   $ print(library)\n    ________________________________________\n   │ 'My Bookshelf'                         │\n   │    ______________________________      │\n   │   │ 'Fiction'                    │     │\n   │   │ topic = 'Fantasy'            │     │\n   │   │    ________________________  │     │\n   │   │   │ 'LOTR'                 │ │     │\n   │   │   │ author = 'JRR Tolkien' │ │     │\n   │   │   │ topic  = 'Hobbits'     │ │     │\n   │   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯  │     │\n   │   │    _______________________   │     │\n   │   │   │ 'Harry Potter'        │  │     │\n   │   │   │ author = 'JK Rowling' │  │     │\n   │   │   │ topic  = 'Wizards'    │  │     │\n   │   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯   │     │\n   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯      │\n   │    __________________________________  │\n   │   │ 'Non-Fiction'                    │ │\n   │   │ topic = 'Physics'                │ │\n   │   │    ____________________________  │ │\n   │   │   │ 'General Relativity'       │ │ │\n   │   │   │ author = 'Albert Einstein' │ │ │\n   │   │   │ topic  = 'Time, Gravity'   │ │ │\n   │   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯  │ │\n   │   │    ___________________________   │ │\n   │   │   │ 'A Brief History of Time' │  │ │\n   │   │   │ author = 'Steven Hawking' │  │ │\n   │   │   │ topic  = 'Black Holes'    │  │ │\n   │   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯   │ │\n   │    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯  │\n    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\n\n\n----------------------------------------------------------------\nAttrs: Plural/singular hybrid attributes and more\n----------------------------------------------------------------\n\nSpend less time deciding between using the singular or plural form for an attribute name:\n\n.. code-block:: python\n\n   tasks = sections('pay bill', 'clean', status=['completed', 'started'])\n   assert tasks.statuses == ['completed', 'started']\n   assert tasks['pay bill'].status == 'completed'\n   assert tasks['clean'].status == 'started'\n\nIf you don’t like this feature, simply turn it off as shown in the **Details - Plural/singular attributes** section.\n\n\n--------------------------------------------------------------------\nProperties: Easily add on the fly\n--------------------------------------------------------------------\n\nProperties and methods are automatically added to all nodes in a structure returned from a ``sections()`` call when passed as keyword arguments:\n\n.. code-block:: python\n\n   schedule = sections(\n       'Weekdays', 'Weekend',\n       hours_per_day=[[8, 8, 6, 10, 8], [4, 6]],\n       hours=property(lambda self: sum(self.hours_per_day)),\n   )\n   assert schedule['Weekdays'].hours == 40\n   assert schedule['Weekend'].hours == 10\n   assert schedule.hours == 50\n\nAdding properties and methods this way doesn’t affect the class definitions of Sections/nodes from other structures. See the **Details - Properties/methods** section for how this works.\n\n\n--------------------------------------------------------------------\nConstruction: Build gradually or all at once\n--------------------------------------------------------------------\n\nConstruct section-by-section, section-wise, attribute-wise, or other ways:\n\n.. code-block:: python\n\n   def demo_different_construction_techniques():\n       \"\"\"Example construction techniques for producing the same structure.\"\"\"\n       # Building section-by-section\n       books = sections()\n       books['LOTR'] = sections(topic='Hobbits', author='JRR Tolkien')\n       books['Harry Potter'] = sections(topic='Wizards', author='JK Rowling')\n       demo_resulting_object_api(books)\n\n       # Section-wise construction\n       books = sections(\n           sections('LOTR', topic='Hobbits', author='JRR Tolkien'),\n           sections('Harry Potter', topic='Wizards', author='JK Rowling')\n       )\n       demo_resulting_object_api(books)\n\n       # Attribute-wise construction\n       books = sections(\n           'LOTR', 'Harry Potter',\n           topics=['Hobbits', 'Wizards'],\n           authors=['JRR Tolkien', 'JK Rowling']\n       )\n       demo_resulting_object_api(books)\n\n       # setattr post-construction\n       books = sections(\n           'LOTR', 'Harry Potter',\n       )\n       books.topics = ['Hobbits', 'Wizards']\n       books['LOTR'].author = 'JRR Tolkien'\n       books['Harry Potter'].author = 'JK Rowling'\n       demo_resulting_object_api(books)\n\n   def demo_resulting_object_api(books):\n       \"\"\"Example Section structure API and expected results.\"\"\"\n       assert books.names == ['LOTR', 'Harry Potter']\n       assert books.topics == ['Hobbits', 'Wizards']\n       assert books.authors == ['JRR Tolkien', 'JK Rowling']\n       assert books['LOTR'].topic == 'Hobbits'\n       assert books['LOTR'].author == 'JRR Tolkien'\n       assert books['Harry Potter'].topic == 'Wizards'\n       assert books['Harry Potter'].author == 'JK Rowling'\n\n   demo_different_construction_techniques()\n\n\n=========================\nDetails\n=========================\n\n\n--------------------------------------------------------------------\nSection names\n--------------------------------------------------------------------\n\nThe non-keyword arguments passed into a ``sections()`` call define the section names and are accessed through the attribute ``name``. The names are used like ``keys`` in a ``dict`` to access each child section of the root section node:\n\n.. code-block:: python\n\n   books = sections(\n       'LOTR', 'Harry Potter',\n       topic=['Hobbits', 'Wizards'],\n       author=['JRR Tolkien', 'JK Rowling']\n   )\n   assert books.names == ['LOTR', 'Harry Potter']\n   assert books['LOTR'].name == 'LOTR'\n   assert books['Harry Potter'].name == 'Harry Potter'\n\nNames are optional, and by default, children names are assigned as integer values corresponding to indices in an array, while a root has a default keyvalue of ``sections.SectionNone``:\n\n.. code-block:: python\n\n   sect = sections(x=['a', 'b'])\n   assert sect.sections.names == [0, 1]\n   assert sect.name is sections.SectionNone\n\n   # the string representation of sections.SectionNone is 'section':\n   assert str(sect.name) == 'sections'\n\n\n----------------------------------------------------------------\nParent names and attributes\n----------------------------------------------------------------\n\nA parent section name can optionally be provided as the first argument in a ``sections()`` call by defining it in a set (surrounding it with curly brackets). This strategy avoids an extra level of braces when instantiating Section objects. This idea applies also for defining parent attributes:\n\n.. code-block:: python\n\n   library = sections(\n       {\"My Bookshelf\"},\n       [{'Fantasy'}, 'LOTR', 'Harry Potter'],\n       [{'Academic'}, 'Advanced Mathematics', 'Physics for Engineers'],\n       topic=[{'All my books'},\n              [{'Imaginary things'}, 'Hobbits', 'Wizards'],\n              [{'School'}, 'Numbers', 'Forces']],\n   )\n   assert library.name == \"My Bookshelf\"\n   assert library.sections.names == ['Fantasy', 'Academic']\n   assert library['Fantasy'].sections.names == ['LOTR', 'Harry Potter']\n   assert library['Academic'].sections.names == [\n       'Advanced Mathematics', 'Physics for Engineers'\n   ]\n   assert library['Fantasy']['Harry Potter'].name == 'Harry Potter'\n   assert library.topic == 'All my books'\n   assert library['Fantasy'].topic == 'Imaginary things'\n   assert library['Academic'].topic == 'School'\n\n\n----------------------------------------------------------------\nReturn attributes as a list, dict, or iterable\n----------------------------------------------------------------\n\nAccess the data in different forms with the ``gettype`` argument in `Section.__call__() \u003chttps://sections.readthedocs.io/en/latest/reference/#sections.Section.__call__\u003e`_ as follows:\n\n.. code-block:: python\n\n   menu = sections('Breakfast', 'Dinner', sides=['HashBrown', 'Fries'])\n\n   # return as list always, even if a single element is returned\n   assert menu('sides', list) == ['HashBrown', 'Fries']\n   assert menu['Breakfast']('side', list) == ['HashBrown']\n\n   # return as dict\n   assert menu('sides', dict) == {'Breakfast': 'HashBrown', 'Dinner': 'Fries'}\n   assert menu['Breakfast']('side', dict) == {'Breakfast': 'HashBrown'}\n\n   # return as iterator over elements in list (fastest method, theoretically)\n   for i, value in enumerate(menu('sides', iter)):\n       assert value == ['HashBrown', 'Fries'][i]\n   for i, value in enumerate(menu['Breakfast']('side', iter)):\n       assert value == ['HashBrown'][i]\n\nSee the `Section.__call__() \u003chttps://sections.readthedocs.io/en/latest/reference/#sections.Section.__call__\u003e`_ method in the References section of the docs for more options.\n\nSet the default return type when accessing structure attributes by changing ``Section.default_gettype`` as follows:\n\n.. code-block:: python\n\n   menu = sections('Breakfast', 'Dinner', sides=['HashBrown', 'Fries'])\n\n   menu['Breakfast'].default_gettype = dict  # set for only 'Breakfast' node\n   assert menu.sides == ['HashBrown', 'Fries']\n   assert menu['Breakfast']('side') == {'Breakfast': 'HashBrown'}\n\n   menu.cls.default_gettype = dict           # set for all nodes in `menu`\n   assert menu('sides') == {'Breakfast': 'HashBrown', 'Dinner': 'Fries'}\n   assert menu['Breakfast']('side') == {'Breakfast': 'HashBrown'}\n\n   sections.Section.default_gettype = dict   # set for all structures\n   tasks1 = sections('pay bill', 'clean', status=['completed', 'started'])\n   tasks2 = sections('pay bill', 'clean', status=['completed', 'started'])\n   assert tasks1('statuses') == {'pay bill': 'completed', 'clean': 'started'}\n   assert tasks2('statuses') == {'pay bill': 'completed', 'clean': 'started'}\n\nThe above will also work for accessing attributes in the form ``object.attr`` but only if the node does not contain the attribute ``attr``, otherwise it will return the non-iterable raw value for ``attr``. Therefore, for consistency, access attributes using `Section.__call__() \u003chttps://sections.readthedocs.io/en/latest/reference/#sections.Section.__call__\u003e`_ like above if you wish to **always receive an iterable** form of the attributes.\n\n\n----------------------------------------------------------------\nPlural/singular attributes\n----------------------------------------------------------------\n\nWhen an attribute is not found in a Section node, both the plural and singular\nforms of the word are then checked to see if the node contains the attribute\nunder those forms of the word. If they are still not found, the node will\nrecursively repeat the same search on each of its children, concatenating the\nresults into a list or dict. The true attribute name in each node supplied a\ncorresponding value is whatever name was given in the keyword argument’s key\n(i.e. ``status`` in the example below).\n\nIf you don’t like this feature, simply turn it off using the following:\n\n.. code-block:: python\n\n   import pytest\n   tasks = sections('pay bill', 'clean', status=['completed', 'started'])\n   assert tasks.statuses == ['completed', 'started']\n   # turn off for all future structures:\n   sections.Section.use_pluralsingular = False\n   tasks = sections('pay bill', 'clean', status=['completed', 'started'])\n   with pytest.raises(AttributeError):\n       tasks.statuses  # this now raises an AttributeError\n\nNote, however, that this will still traverse descendant nodes to see if they\ncontain the requested attribute. To stop using this feature also, access\nattributes using the `Section.get_node_attr() \u003chttps://sections.readthedocs.io/en/latest/reference/#sections.Section.get_node_attr\u003e`_ method instead.\n\n\n----------------------------------------------------------------\nProperties/methods\n----------------------------------------------------------------\n\nEach ``sections()`` call returns a structure containing nodes of a unique class created in a class factory function, where the unique class definition contains no logic except that it inherits from the Section class. This allows properties/methods added to one structure’s class definition to not affect the class definitions of nodes from other structures.\n\n\n----------------------------------------------------------------\nSubclassing\n----------------------------------------------------------------\n\nInheriting Section is easy, the only requirement is to call ``super().__init__(**kwds)`` at some point in ``__init__()``  like below if you override that method:\n\n.. code-block:: python\n\n   class Library(sections.Section):\n       \"\"\"My library class.\"\"\"\n\n       def __init__(self, price=\"Custom default value\", **kwds):\n           \"\"\"Pass **kwds to super.\"\"\"\n           super().__init__(**kwds)\n           self.price = price\n\n       @property\n       def genres(self):\n           \"\"\"A synonym for sections.\"\"\"\n           if self.isroot:\n               return self.sections\n           else:\n               raise AttributeError('This library has only 1 level of genres')\n\n       @property\n       def books(self):\n           \"\"\"A synonym for leaves.\"\"\"\n           return self.leaves\n\n       @property\n       def titles(self):\n           \"\"\"A synonym for names.\"\"\"\n           return self.leaves.names\n\n       def critique(self, review=\"Haven't read it yet\", rating=0):\n           \"\"\"Set the book price based on the rating.\"\"\"\n           self.review = review\n           self.price = rating * 2\n\n   library = Library(\n       [{'Fantasy'}, 'LOTR', 'Harry Potter'],\n       [{'Academic'}, 'Advanced Math.', 'Physics for Engineers']\n   )\n   assert library.genres.names == ['Fantasy', 'Academic']\n   assert library.books.titles == [\n       'LOTR', 'Harry Potter', 'Advanced Math.', 'Physics for Engineers'\n   ]\n   library.books['LOTR'].critique(review='Good but too long', rating=7)\n   library.books['Harry Potter'].critique(\n       review=\"I don't like owls\", rating=4)\n   assert library.books['LOTR'].review == 'Good but too long'\n   assert library.books['LOTR'].price == 14\n   assert library.books['Harry Potter'].review == \"I don't like owls\"\n   assert library.books['Harry Potter'].price == 8\n   import pytest\n   with pytest.raises(AttributeError):\n       library['Fantasy'].genres\n\n``Section.__init__()`` assigns the kwds values passed to it to the object attributes, and the passed kwds are generated during instantiation by a metaclass.\n\n\n----------------------------------------------------------------\nPerformance\n----------------------------------------------------------------\n\nEach non-leaf Section node keeps a cache containing quickly readable references to attribute dicts previously parsed from manually traversing through descendant nodes in an earlier read. The caches are invalidated accordingly for modified nodes and their ancestors when the tree structure or node attribute values change.\n\nThe caches allow instant reading of sub-lists/dicts in Θ(1) time and can often\nmake structure attribute reading faster by 5x, or even much more when the\nstructure is rarely being modified.\nIf preferred, turn this feature off to avoid the extra memory consumption it causes by modifying the node or structure’s class attribute ``use_cache`` to ``False`` as follows:\n\n.. code-block:: python\n\n   sect = sections(*[[[[[42] * 10] * 10] * 10] * 10])\n   sect.use_cache = False              # turn off for just the root node\n   sect.cls.use_cache = False          # turn off for all nodes in `sect`\n   sections.Section.use_cache = False  # turn off for all structures\n","funding_links":["https://github.com/sponsors/trevorpogue"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevorpogue%2Fsections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrevorpogue%2Fsections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevorpogue%2Fsections/lists"}