Graphinate 0.15.3__tar.gz → 0.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. {graphinate-0.15.3 → graphinate-0.16.0}/CHANGELOG.md +10 -0
  2. {graphinate-0.15.3 → graphinate-0.16.0}/PKG-INFO +4 -3
  3. {graphinate-0.15.3 → graphinate-0.16.0}/README.md +2 -2
  4. graphinate-0.16.0/design/proposals/GEP-003_Roadmap.md +122 -0
  5. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-010_Explicit_Dependency_Injection.md +10 -9
  6. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-016_CLI_Argument_Parsing_and_Path_Safety.md +2 -1
  7. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-017_Decoupled_Server_Architecture.md +24 -4
  8. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-031_High_Performance_Vectorized_Operations.md +9 -3
  9. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-032_GraphModel_Compilation_and_Validation_Lifecycle.md +5 -3
  10. graphinate-0.16.0/docs/usage/cli.md +56 -0
  11. {graphinate-0.15.3 → graphinate-0.16.0}/docs/usage/lib.md +1 -1
  12. {graphinate-0.15.3 → graphinate-0.16.0}/pyproject.toml +3 -1
  13. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/.agents/skills/graphinate/SKILL.md +3 -3
  14. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/graphql.py +39 -26
  15. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/networkx.py +27 -10
  16. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/modeling.py +63 -24
  17. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/renderers/graphql.py +23 -7
  18. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/builders/test_graphql.py +97 -0
  19. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/renderers/test_graphql_server.py +2 -3
  20. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_modeling.py +114 -6
  21. graphinate-0.16.0/tests/graphinate/test_tools.py +63 -0
  22. graphinate-0.16.0/uv.lock +3788 -0
  23. graphinate-0.15.3/design/proposals/GEP-003_Roadmap.md +0 -84
  24. graphinate-0.15.3/docs/usage/cli.md +0 -53
  25. {graphinate-0.15.3 → graphinate-0.16.0}/.coveragerc +0 -0
  26. {graphinate-0.15.3 → graphinate-0.16.0}/.deepsource.toml +0 -0
  27. {graphinate-0.15.3 → graphinate-0.16.0}/.github/dependabot.yml +0 -0
  28. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/codeql.yml +0 -0
  29. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/codspeed.yml +0 -0
  30. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/publish-docs.yaml +0 -0
  31. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/publish.yml +0 -0
  32. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/test-beta.yml +0 -0
  33. {graphinate-0.15.3 → graphinate-0.16.0}/.github/workflows/test.yml +0 -0
  34. {graphinate-0.15.3 → graphinate-0.16.0}/.gitignore +0 -0
  35. {graphinate-0.15.3 → graphinate-0.16.0}/.python-version +0 -0
  36. {graphinate-0.15.3 → graphinate-0.16.0}/.sonarcloud.properties +0 -0
  37. {graphinate-0.15.3 → graphinate-0.16.0}/AGENTS.md +0 -0
  38. {graphinate-0.15.3 → graphinate-0.16.0}/LICENSE +0 -0
  39. {graphinate-0.15.3 → graphinate-0.16.0}/PERSONA.md +0 -0
  40. {graphinate-0.15.3 → graphinate-0.16.0}/PRINCIPLES.md +0 -0
  41. {graphinate-0.15.3 → graphinate-0.16.0}/STYLEGUIDE.md +0 -0
  42. {graphinate-0.15.3 → graphinate-0.16.0}/TELEMETRY.md +0 -0
  43. {graphinate-0.15.3 → graphinate-0.16.0}/TOOLS.md +0 -0
  44. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/color_benchmark.py +0 -0
  45. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/conftest.py +0 -0
  46. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/test_builders_benchmark.py +0 -0
  47. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/test_color_benchmark.py +0 -0
  48. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/test_converters_benchmark.py +0 -0
  49. {graphinate-0.15.3 → graphinate-0.16.0}/benchmarks/test_modeling_benchmark.py +0 -0
  50. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/Builders_Analysis.md +0 -0
  51. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/CLI_Analysis.md +0 -0
  52. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/Core_Analysis.md +0 -0
  53. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/GraphModel_Analysis.md +0 -0
  54. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/Renderers_Analysis.md +0 -0
  55. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/Server_Analysis.md +0 -0
  56. {graphinate-0.15.3 → graphinate-0.16.0}/design/analysis/Viewer_Analysis.md +0 -0
  57. {graphinate-0.15.3 → graphinate-0.16.0}/design/art_prompts.md +0 -0
  58. {graphinate-0.15.3 → graphinate-0.16.0}/design/art_prompts_v1.md +0 -0
  59. {graphinate-0.15.3 → graphinate-0.16.0}/design/art_prompts_v2.md +0 -0
  60. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Builders_Grading.md +0 -0
  61. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/CLI_Grading.md +0 -0
  62. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Core_Grading.md +0 -0
  63. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Modeling_Grading.md +0 -0
  64. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Renderers_Grading.md +0 -0
  65. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Server_Grading.md +0 -0
  66. {graphinate-0.15.3 → graphinate-0.16.0}/design/grading/Viewer_Grading.md +0 -0
  67. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/01_The_Foundations.md +0 -0
  68. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/02_The_Fantastical.md +0 -0
  69. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/03_The_Analog.md +0 -0
  70. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/04_The_Avant_Garde.md +0 -0
  71. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/05_The_Pop_Culture.md +0 -0
  72. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/06_The_Modern.md +0 -0
  73. {graphinate-0.15.3 → graphinate-0.16.0}/design/prompts/07_Graphinate_2.0.md +0 -0
  74. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-000_Graphinate_Enhancement_Proposals.md +0 -0
  75. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-001_The_Graphinate_Mission.md +0 -0
  76. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-002_Modularization_Strategy.md +0 -0
  77. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-007_Theoretical_Foundations.md +0 -0
  78. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-011_Standardized_Element_Factory.md +0 -0
  79. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-012_Robust_Model_Composition.md +0 -0
  80. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-013_Stable_ID_Serialization.md +0 -0
  81. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-014_Explicit_GraphQL_Schema_Generation.md +0 -0
  82. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-015_Refactored_Graph_Population_Strategies.md +0 -0
  83. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-018_Relay_Support.md +0 -0
  84. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-019_Offline_Viewer_Support.md +0 -0
  85. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-020_Modular_Frontend_Architecture.md +0 -0
  86. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-022_2D_Visualization_Mode.md +0 -0
  87. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-023_Async_Support.md +0 -0
  88. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-024_The_Graph_Engine.md +0 -0
  89. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-025_Signed_ID_Serialization.md +0 -0
  90. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-026_Pluggable_Visualization_and_Notebook_Support.md +0 -0
  91. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-027_Advanced_ThreeJS_Visualizations.md +0 -0
  92. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-028_MCP_Server_Integration.md +0 -0
  93. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-029_Extraction_Strategies.md +0 -0
  94. {graphinate-0.15.3 → graphinate-0.16.0}/design/proposals/GEP-030_Node_Type_Taxonomy.md +0 -0
  95. {graphinate-0.15.3 → graphinate-0.16.0}/docs/acknowledgements.md +0 -0
  96. {graphinate-0.15.3 → graphinate-0.16.0}/docs/advanced/modeling.md +0 -0
  97. {graphinate-0.15.3 → graphinate-0.16.0}/docs/advanced/optimization.md +0 -0
  98. {graphinate-0.15.3 → graphinate-0.16.0}/docs/advanced/secure_ids.md +0 -0
  99. {graphinate-0.15.3 → graphinate-0.16.0}/docs/assets/badge/v0.json +0 -0
  100. {graphinate-0.15.3 → graphinate-0.16.0}/docs/assets/images/logo-128.png +0 -0
  101. {graphinate-0.15.3 → graphinate-0.16.0}/docs/assets/images/network_graph.png +0 -0
  102. {graphinate-0.15.3 → graphinate-0.16.0}/docs/assets/stylesheets/extra.css +0 -0
  103. {graphinate-0.15.3 → graphinate-0.16.0}/docs/development.md +0 -0
  104. {graphinate-0.15.3 → graphinate-0.16.0}/docs/diagrams/class_diagram.md +0 -0
  105. {graphinate-0.15.3 → graphinate-0.16.0}/docs/diagrams/component_diagram.md +0 -0
  106. {graphinate-0.15.3 → graphinate-0.16.0}/docs/diagrams/function_call_graph.md +0 -0
  107. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/code.md +0 -0
  108. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/github.md +0 -0
  109. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/math.md +0 -0
  110. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/social.md +0 -0
  111. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/system.md +0 -0
  112. {graphinate-0.15.3 → graphinate-0.16.0}/docs/examples/web.md +0 -0
  113. {graphinate-0.15.3 → graphinate-0.16.0}/docs/gen_ref_pages.py +0 -0
  114. {graphinate-0.15.3 → graphinate-0.16.0}/docs/index.md +0 -0
  115. {graphinate-0.15.3 → graphinate-0.16.0}/docs/introduction.md +0 -0
  116. {graphinate-0.15.3 → graphinate-0.16.0}/docs/start.md +0 -0
  117. {graphinate-0.15.3 → graphinate-0.16.0}/docs/tutorial.md +0 -0
  118. {graphinate-0.15.3 → graphinate-0.16.0}/examples/code/git_commits.py +0 -0
  119. {graphinate-0.15.3 → graphinate-0.16.0}/examples/code/python_ast.py +0 -0
  120. {graphinate-0.15.3 → graphinate-0.16.0}/examples/code/python_dependencies.py +0 -0
  121. {graphinate-0.15.3 → graphinate-0.16.0}/examples/code/requirements.txt +0 -0
  122. {graphinate-0.15.3 → graphinate-0.16.0}/examples/code/tokens.py +0 -0
  123. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/_client.py +0 -0
  124. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/commits_visibilty_graph.py +0 -0
  125. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/followers.graphql +0 -0
  126. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/followers.py +0 -0
  127. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/graphql.config.yml +0 -0
  128. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/repositories.graphql +0 -0
  129. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/repositories.py +0 -0
  130. {graphinate-0.15.3 → graphinate-0.16.0}/examples/github/requirements.txt +0 -0
  131. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/_test_materializers.py +0 -0
  132. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/graph_atlas.py +0 -0
  133. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/graphs.py +0 -0
  134. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/gui.py +0 -0
  135. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/materializers.py +0 -0
  136. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/polygonal_graph.py +0 -0
  137. {graphinate-0.15.3 → graphinate-0.16.0}/examples/math/requirements.txt +0 -0
  138. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/13/dd/73ce25face7beb30b69b64feeb77.val +0 -0
  139. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/21/9e/00846f323987ba16cfbe0127d8eb.val +0 -0
  140. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/70/b6/2aefb0269adce7fedf877fa0d267.val +0 -0
  141. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/87/f5/ec1739bc369e84c3fcb302bf532a.val +0 -0
  142. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/ba/fe/3aca7b2c38abff60e7ce5eb486a8.val +0 -0
  143. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/c7/9e/ce82b0288020b7152779df09bd73.val +0 -0
  144. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/cache.db +0 -0
  145. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/d2/53/3b88f2fc162561cfdbbe9abc352a.val +0 -0
  146. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/cache/e2/d5/5d079f200eabf9b625b0473f6fbe.val +0 -0
  147. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/gui.py +0 -0
  148. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/music_artists.py +0 -0
  149. {graphinate-0.15.3 → graphinate-0.16.0}/examples/social/requirements.txt +0 -0
  150. {graphinate-0.15.3 → graphinate-0.16.0}/examples/system/.ignore +0 -0
  151. {graphinate-0.15.3 → graphinate-0.16.0}/examples/system/files.py +0 -0
  152. {graphinate-0.15.3 → graphinate-0.16.0}/examples/system/processes.py +0 -0
  153. {graphinate-0.15.3 → graphinate-0.16.0}/examples/system/requirements.txt +0 -0
  154. {graphinate-0.15.3 → graphinate-0.16.0}/examples/web/html_dom.py +0 -0
  155. {graphinate-0.15.3 → graphinate-0.16.0}/examples/web/page_links.py +0 -0
  156. {graphinate-0.15.3 → graphinate-0.16.0}/examples/web/requirements.txt +0 -0
  157. {graphinate-0.15.3 → graphinate-0.16.0}/playground/check_repo.py +0 -0
  158. {graphinate-0.15.3 → graphinate-0.16.0}/playground/ethernet/traceroute.py +0 -0
  159. {graphinate-0.15.3 → graphinate-0.16.0}/playground/genric_graph.graphql +0 -0
  160. {graphinate-0.15.3 → graphinate-0.16.0}/playground/graphql.config.yml +0 -0
  161. {graphinate-0.15.3 → graphinate-0.16.0}/playground/house_of_graphs.py +0 -0
  162. {graphinate-0.15.3 → graphinate-0.16.0}/playground/octagonal_graph.py +0 -0
  163. {graphinate-0.15.3 → graphinate-0.16.0}/playground/science/caffeine.py +0 -0
  164. {graphinate-0.15.3 → graphinate-0.16.0}/playground/social/db.xml +0 -0
  165. {graphinate-0.15.3 → graphinate-0.16.0}/playground/social/music.py +0 -0
  166. {graphinate-0.15.3 → graphinate-0.16.0}/playground/social/requirements.txt +0 -0
  167. {graphinate-0.15.3 → graphinate-0.16.0}/playground/text/nlp_graph.py +0 -0
  168. {graphinate-0.15.3 → graphinate-0.16.0}/playground/text/requirements.txt +0 -0
  169. {graphinate-0.15.3 → graphinate-0.16.0}/playground/time_series/requirements.txt +0 -0
  170. {graphinate-0.15.3 → graphinate-0.16.0}/playground/time_series/visibility_graph.py +0 -0
  171. {graphinate-0.15.3 → graphinate-0.16.0}/playground/word_graph.py +0 -0
  172. {graphinate-0.15.3 → graphinate-0.16.0}/properdocs.yml +0 -0
  173. {graphinate-0.15.3 → graphinate-0.16.0}/sonar-project.properties +0 -0
  174. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/__init__.py +0 -0
  175. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/__main__.py +0 -0
  176. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/_secure.py +0 -0
  177. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/__init__.py +0 -0
  178. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/builder.py +0 -0
  179. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/d3.py +0 -0
  180. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/builders/mermaid.py +0 -0
  181. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/cli.py +0 -0
  182. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/color.py +0 -0
  183. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/constants.py +0 -0
  184. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/converters.py +0 -0
  185. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/enums.py +0 -0
  186. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/renderers/__init__.py +0 -0
  187. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/renderers/matplotlib.py +0 -0
  188. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/__init__.py +0 -0
  189. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/starlette/__init__.py +0 -0
  190. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/starlette/views.py +0 -0
  191. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/__init__.py +0 -0
  192. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/elements/index.html +0 -0
  193. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/graphiql/index.html +0 -0
  194. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/rapidoc/index.html +0 -0
  195. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/static/images/logo-128.png +0 -0
  196. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/static/images/logo.svg +0 -0
  197. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/static/images/network_graph.png +0 -0
  198. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/viewer/index.html +0 -0
  199. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/server/web/voyager/index.html +0 -0
  200. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/tools.py +0 -0
  201. {graphinate-0.15.3 → graphinate-0.16.0}/src/graphinate/typing.py +0 -0
  202. {graphinate-0.15.3 → graphinate-0.16.0}/tests/conftest.py +0 -0
  203. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/builders/test_build.py +0 -0
  204. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/builders/test_d3.py +0 -0
  205. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/builders/test_mermaid.py +0 -0
  206. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/builders/test_networkx.py +0 -0
  207. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/renderers/test_matplotlib_draw.py +0 -0
  208. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/renderers/test_matplotlib_plot.py +0 -0
  209. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/server/test_starlette.py +0 -0
  210. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_cli.py +0 -0
  211. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_color.py +0 -0
  212. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_converters.py +0 -0
  213. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_modeling_add.py +0 -0
  214. {graphinate-0.15.3 → graphinate-0.16.0}/tests/graphinate/test_server.py +0 -0
@@ -1,3 +1,13 @@
1
+ ## v0.16.0 (2026-10-17)
2
+
3
+ ### Feat
4
+
5
+ - **GEP-017**: Exposes `create_app` for Starlette application setup.
6
+
7
+ ### Fix
8
+
9
+ - Adds model compilation and refines node and GraphQL builders.
10
+
1
11
  ## v0.15.3 (2026-10-17)
2
12
 
3
13
  ### Fix
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: Graphinate
3
- Version: 0.15.3
3
+ Version: 0.16.0
4
4
  Summary: Graphinate. Data to Graphs.
5
5
  Project-URL: Homepage, https://erivlis.github.io/graphinate
6
6
  Project-URL: Documentation, https://erivlis.github.io/graphinate
7
7
  Project-URL: Bug Tracker, https://github.com/erivlis/graphinate/issues
8
8
  Project-URL: Source, https://github.com/erivlis/graphinate
9
9
  Author-email: Eran Rivlis <eran@rivlis.info>
10
+ License-Expression: LGPL-3.0-or-later
10
11
  License-File: LICENSE
11
12
  Keywords: declarative,graph
12
13
  Classifier: Development Status :: 4 - Beta
@@ -208,7 +209,7 @@ or
208
209
  uv add graphinate[server]
209
210
  ```
210
211
 
211
- **Graphinate** officially supports Python >= 3.10.
212
+ **Graphinate** officially supports Python >= 3.11.
212
213
 
213
214
  ### Example
214
215
 
@@ -251,7 +252,7 @@ mermaid_markdown: str = graphinate.mermaid.markdown(diagram)
251
252
  mermaid_html: str = graphinate.mermaid.html(diagram, title=graph_model.name)
252
253
 
253
254
  # or use the GraphQL Builder
254
- builder = graphinate.builders.GraphQLBuilder(.graph_model)
255
+ builder = graphinate.builders.GraphQLBuilder(graph_model)
255
256
 
256
257
  # to create a Strawberry GraphQL schema
257
258
  schema = builder.build()
@@ -159,7 +159,7 @@ or
159
159
  uv add graphinate[server]
160
160
  ```
161
161
 
162
- **Graphinate** officially supports Python >= 3.10.
162
+ **Graphinate** officially supports Python >= 3.11.
163
163
 
164
164
  ### Example
165
165
 
@@ -202,7 +202,7 @@ mermaid_markdown: str = graphinate.mermaid.markdown(diagram)
202
202
  mermaid_html: str = graphinate.mermaid.html(diagram, title=graph_model.name)
203
203
 
204
204
  # or use the GraphQL Builder
205
- builder = graphinate.builders.GraphQLBuilder(.graph_model)
205
+ builder = graphinate.builders.GraphQLBuilder(graph_model)
206
206
 
207
207
  # to create a Strawberry GraphQL schema
208
208
  schema = builder.build()
@@ -0,0 +1,122 @@
1
+ # GEP-003: Graphinate Development Roadmap
2
+
3
+ | Field | Value |
4
+ |:------------|:------------------------|
5
+ | **GEP** | 3 |
6
+ | **Title** | Development Roadmap |
7
+ | **Author** | Eran Rivlis |
8
+ | **Status** | Active |
9
+ | **Type** | Process |
10
+ | **Created** | 2025-12-30 |
11
+ | **Updated** | 2026-10-17 |
12
+
13
+ ## Abstract
14
+
15
+ This document outlines the strategic execution plan for the Graphinate Enhancement Proposals (GEPs). It serves as a living document to track the prioritization and status of major initiatives.
16
+
17
+ ## Phase 1: Stabilization (Completed)
18
+
19
+ *Focus: Fixing brittle code, security risks, and "bad magic". All Phase 1 stabilization proposals (**GEP-010**, **GEP-016**, **GEP-017**, **GEP-025**, **GEP-032**) have been implemented and archived.*
20
+
21
+ ## Phase 2: Core Spine & Architectural Health
22
+
23
+ *Focus: Strengthening the core execution engine, schema generation, and modularity.*
24
+
25
+ | GEP | Title | Impact | Difficulty | Rationale |
26
+ |:------------|:----------------------------------------------|:---------|:-----------|:-------------------------------------------------------------------------------------------------------------------------------------|
27
+ | **GEP-024** | **The Graph Engine** | **High** | **Hard** | **Core Architecture.** Extracting the execution logic is the foundation for Async, Realtime, and better testing. Supersedes GEP-015. |
28
+ | **GEP-014** | **Explicit GraphQL Schema Generation** | **High** | **Hard** | **Major Refactor.** Refactoring dynamic class generation to `strawberry.tools.create_type` and context-injected resolvers. |
29
+ | **GEP-023** | **Async Support** | **High** | **Hard** | **Performance.** Massive gains for I/O-bound crawling (`async def` generators). Dependent on GEP-024. |
30
+ | **GEP-019** | **Offline Viewer Support** | Medium | Easy | **Reliability.** GraphiQL/Voyager are vendored; completing local vendoring for `viewer` removes remaining CDN dependencies. |
31
+ | **GEP-031** | **High-Performance Vectorized Operations** | Medium | Medium | **Performance.** Progressive enhancement via optional NumPy/Numba (partially active in `color.py`). |
32
+ | **GEP-011** | **Standardized Element Factory** | Low | Easy | **Cleanup.** Minor API improvement for element tuple construction. |
33
+ | **GEP-012** | **Robust Model Composition** | Low | Easy | **Cleanup.** Handling advanced edge cases in `ModelA + ModelB` merging. |
34
+
35
+ ## Phase 3: Focused Expansion
36
+
37
+ *Focus: Practical extensions that build directly on the core engine and schema.*
38
+
39
+ | GEP | Title | Impact | Difficulty | Rationale |
40
+ |:------------|:----------------------------------|:---------|:-----------|:-------------------------------------------------------------------------------------------------------|
41
+ | **GEP-018** | **Relay Support** | **High** | Medium | **Enterprise Feature.** Makes the API compatible with modern frontend clients (React Relay, Apollo). |
42
+ | **GEP-020** | **Modular Frontend Architecture** | Medium | Medium | **Maintainability.** Splitting the monolithic `viewer/index.html` into maintainable ES modules. |
43
+ | **GEP-029** | **Extraction Strategies** | Medium | Medium | **Ergonomics.** Lightweight iterator/generator helpers (`scan`, `walk`, `listen`) or cookbook recipes. |
44
+
45
+ ## Phase 4: Exploratory & Spin-Off Track (Ideation / Brainstorming)
46
+
47
+ *Focus: Ideas originating from exploratory brainstorming sessions. Rather than bloating the core `graphinate` package, these proposals should be treated as candidates for standalone companion packages, external integrations, or example recipes.*
48
+
49
+ | GEP | Title | Recommended Vehicle | Rationale |
50
+ |:------------|:-----------------------------------------------|:-------------------------------------|:------------------------------------------------------------------------------------------------------------------------------|
51
+ | **GEP-026** | **Pluggable Visualization & Notebook Support** | Companion Package / Cookbook Recipes | Keeps core `graphinate` free of heavy frontend widget toolchains while enabling Jupyter/Cosmograph recipes. |
52
+ | **GEP-027** | **Advanced Three.js Visualizations** | Standalone Frontend / Demo Showcase | Custom WebGL shaders, procedural cities, and time tunnels belong in a dedicated visualization showcase, not a Python ETL lib. |
53
+ | **GEP-028** | **MCP Server Integration** | Companion Package (`graphinate-mcp`) | Ideal as a lightweight wrapper or example recipe exposing `GraphModel` and `GraphQLBuilder` to LLM agents via `fastmcp`. |
54
+ | **GEP-030** | **Node Type Taxonomy** | Cookbook Recipes / Domain Extensions | Universal ontologies (C4, Schema.org, Pygments) are best demonstrated as user-space models/recipes rather than core classes. |
55
+
56
+ ---
57
+
58
+ ## Strategic Assessment of the GEP Trajectory
59
+
60
+ ### 1. Engineering Assessment: The Three Buckets
61
+
62
+ The trajectory from **Graphinate 0.x** (a synchronous, decorator-based NetworkX/GraphQL builder) toward **Graphinate 2.0** (an observable, async-capable, type-safe Graph Synthesis Engine) is architecturally cohesive and well-differentiated in the Python ecosystem—provided that **implementation consolidation takes precedence over scope expansion**.
63
+
64
+ Across the GEP archive, proposals fall naturally into three distinct tiers:
65
+
66
+ 1. **The Core Structural Spine (`GEP-010` → `GEP-032` → `GEP-012` → `GEP-024` → `GEP-023`)**:
67
+ - This is the true heart of Graphinate: moving from implicit `_id` naming magic to explicit `ParentId` dependency injection (**GEP-010**), compiling immutable validated models (**GEP-032**), decoupling traversal (`GraphEngine`, **GEP-024**) from storage (`NetworkxBuilder`), and unlocking `async def` generators (**GEP-023**).
68
+ - Every link in this chain directly enables the next and strengthens the library's core value proposition.
69
+ 2. **Boundary & Hygiene Refactorings (`GEP-002`, `GEP-014`, `GEP-016`, `GEP-017`, `GEP-019`, `GEP-020`, `GEP-031`)**:
70
+ - Practical engineering debt paydown: exposing `create_app()` (**GEP-017**), replacing fragile dynamic class construction in `GraphQLBuilder` with `strawberry.tools.create_type` and context injection (**GEP-014**), and keeping heavy dependencies optional (**GEP-002**).
71
+ 3. **Exploratory Ideation / Ambition Creep (`GEP-026`, `GEP-027`, `GEP-028`, `GEP-029`, `GEP-030`)**:
72
+ - These proposals originated in exploratory brainstorming sessions. While intellectually rich, features like custom Three.js shaders/procedural cities (**GEP-027**), universal class ontologies mapping Schema.org/C4/Pygments (**GEP-030**), and multi-engine WebGL abstractions (**GEP-026**) risk pulling a focused ~1,200-line Python library into maintaining a sprawling JavaScript/WebGL framework or ontology library ("Second-System Syndrome").
73
+ - **Strategic Decision:** Treat Tier 3 proposals as **Exploratory / Spin-Off Candidates** (separate high-level projects, plugin packages, or cookbook examples) so the core library stays lean and faithful to **GEP-001**.
74
+
75
+ ### 2. The Council's Opinion (By Pillar)
76
+
77
+ Evaluating the codebase and GEP trajectory through the **7 Pillars of the Council Framework** (**GEP-001**):
78
+
79
+ | Council Member | Verdict | Critique & Trajectory Guidance |
80
+ |:-------------------------------|:-------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
81
+ | **1. Symmetry (Noether)** | **A-** | `@model.node` supports `ParentId`, hierarchy, multiplicity, and deferred compilation (**GEP-010**, **GEP-032**). Future work in **GEP-024** should consider bringing symmetric dependency/scoping ergonomics to `@model.edge`, and ensuring edges can naturally target hierarchical (`unique=False`) nodes whose keys are lineage tuples. |
82
+ | **2. Falsifiability (Popper)** | **A** | High branch coverage (~98%) and property-style tests provide a strong safety net. Every Standards Track GEP must be paired with falsifying regression tests before its status transitions to `Active`. |
83
+ | **3. Efficiency (Shannon)** | **A-** | `color.py` (**GEP-031**), `VariableNameManager` (`tools.py`), and $O(\text{deg}(v))$ GraphQL neighbor resolution are fast and lean. The primary remaining efficiency win is **GEP-002**: moving heavy packages (`matplotlib`, `strawberry-graphql`) out of mandatory core install dependencies into optional extras. |
84
+ | **4. Safety (The Golem)** | **A-** | With **GEP-025** (HMAC-SHA256 signed IDs), **GEP-032** (immutable `model.compile()` clones), and isolated `GraphNodeType` schema enums, thread-safety and concurrency hazards have been substantially hardened—critical for Python 3.13t–3.15t free-threaded execution. |
85
+ | **5. Clarity (Feynman)** | **A** | The `@model.node` / `@model.edge` declarative DSL remains simple and expressive. Completing **GEP-014** (explicit GraphQL schema generation with context injection) will remove the last major pocket of closure metaprogramming in `builders/graphql.py`. |
86
+ | **6. Consistency (Russell)** | **A-** | Case-insensitive parent resolution and explicit `_lineage` tuple propagation across recursion depths have unified behavior between `modeling.py` and `builders/networkx.py`. |
87
+ | **7. Harmony (The Steward)** | **A-** | **The Dennis Point:** Resist building custom 3D shaders (**GEP-027**) or rigid taxonomies (**GEP-030**) inside core before the execution spine (`compile()` → `GraphEngine` → `abuild()`) is complete. Keep Graphinate focused on its mission: *"A declarative, Pythonic ETL engine for synthesizing graphs from hierarchical data."* |
88
+
89
+ ---
90
+
91
+ ## Summary Priority Matrix (Core Library)
92
+
93
+ | Priority | GEP | Title | Phase |
94
+ |:---------|:------------|:-------------------------------------------|:--------|
95
+ | 1 | **GEP-024** | The Graph Engine | Phase 2 |
96
+ | 2 | **GEP-014** | Explicit GraphQL Schema Generation | Phase 2 |
97
+ | 3 | **GEP-023** | Async Support | Phase 2 |
98
+ | 4 | **GEP-019** | Offline Viewer Support | Phase 2 |
99
+ | 5 | **GEP-031** | High-Performance Vectorized Operations | Phase 2 |
100
+ | 6 | **GEP-018** | Relay Support | Phase 3 |
101
+ | 7 | **GEP-020** | Modular Frontend Architecture | Phase 3 |
102
+ | 8 | **GEP-029** | Extraction Strategies (Helpers / Recipes) | Phase 3 |
103
+
104
+ ## Archive (Completed / Withdrawn / Superseded)
105
+
106
+ | GEP | Title | Status | Reason |
107
+ |:------------|:---------------------------------------|:------------------|:-----------------------------------------------------------------------|
108
+ | **GEP-000** | Graphinate Enhancement Proposals | **Active** | Process definition. |
109
+ | **GEP-001** | The Graphinate Mission | **Active** | Mission statement. |
110
+ | **GEP-002** | Modularization Strategy | **Active** | Architectural principle. |
111
+ | **GEP-007** | Theoretical Foundations | **Active** | Informational / Theory. |
112
+ | **GEP-010** | Explicit Dependency Injection | **Active** | Implemented explicit dependency injection using `ParentId`. |
113
+ | **GEP-013** | Stable ID Serialization | **Withdrawn** | `repr()` deemed sufficient and more robust for Python types. |
114
+ | **GEP-016** | CLI Argument Parsing & Path Safety | **Active** | Implemented Typer CLI with safe dynamic argument parsing & paths. |
115
+ | **GEP-017** | Decoupled Server Architecture | **Active** | Exposed `create_app()` ASGI factory in `graphinate.renderers.graphql`. |
116
+ | **GEP-025** | Signed ID Serialization | **Active** | Implemented as an optional, opt-in HMAC-SHA256 signature layer. |
117
+ | **GEP-032** | GraphModel Compilation & Validation | **Active** | Implemented `GraphModel.compile()` and deferred validation. |
118
+ | **GEP-015** | Refactored Graph Population Strategies | **Superseded** | Replaced by **GEP-024** (The Graph Engine). |
119
+ | **GEP-022** | 2D Visualization Mode | **Superseded** | Replaced by **GEP-026** (Pluggable Visualization). |
120
+ | **GEP-027** | Advanced Three.js Visualizations | **Informational** | Exploratory design concepts (Spin-Off / Showcase Track). |
121
+
122
+
@@ -5,10 +5,10 @@
5
5
  | **GEP** | 10 |
6
6
  | **Title** | Explicit Dependency Injection |
7
7
  | **Author** | Eran Rivlis |
8
- | **Status** | Accepted |
8
+ | **Status** | Active |
9
9
  | **Type** | Standards Track |
10
10
  | **Created** | 2025-12-25 |
11
- | **Updated** | 2026-06-21 |
11
+ | **Updated** | 2026-10-17 |
12
12
 
13
13
  ## Abstract
14
14
 
@@ -144,16 +144,17 @@ and resolved within [modeling.py](file:///C:/dev/erivlis/graphinate/src/graphina
144
144
  * 2025-12-25: Initial Draft
145
145
  * 2026-06-21: Accepted Option 2 (Type Hinting) and implemented the injection engine.
146
146
  * 2026-06-22: Documented remaining structural and validation constraints as open issues/future work.
147
+ * 2026-10-17: Resolved all 5 open issues (deferred compilation validation via GEP-032, immutable `compile()` clone, explicit `_lineage` tuple tracking for non-unique recursive nodes, case-insensitive lineage resolution, and removal of `NodeModel.parameters`).
147
148
 
148
- ## Open Issues & Future Work
149
+ ## Resolved Issues (Formerly Open Issues)
149
150
 
150
- During the design review of the GEP-010 implementation, the Council of Principles identified the following remaining issues and areas for future work:
151
+ During the initial design review of the GEP-010 implementation, the Council of Principles identified five follow-up items, all of which have now been resolved:
151
152
 
152
- 1. **Eager Registration Validation:** Parameter validation (`_validate_node_dependency_registration`) currently runs eagerly at decorator execution time. This creates a strict topological import/decoration order constraint and prevents modular model composition (e.g., merging `ModelA + ModelB` where cross-dependencies are resolved only after composition). Future work will explore deferring validation to graph build time.
153
- 2. **In-place Model Mutation (`rectify`):** The builder mutates the shared `GraphModel` instance in-place during the build phase via `rectify()`. This represents a potential concurrency side-effect. Future updates should clone the model or perform mutations on localized registries.
154
- 3. **Recursive Lineage Namespace Collision:** In recursive structures (e.g., `folder -> folder`), overwriting the `folder_id` key in `kwargs` destroys the parent lineage chain, which causes parent ID resolution to fail when nodes are registered with `unique=False`. Passing an explicit lineage tuple during traversal will solve this collision.
155
- 4. **Lineage Casing Asymmetry:** While injection lookup is case-insensitive, the early-break check in `_parent_node_id` remains case-sensitive, creating potential lineage pollution for mixed-case types.
156
- 5. **Dead Code Cleanup (`NodeModel.parameters`):** The `parameters` attribute on `NodeModel` is currently obsolete and should be cleaned up.
153
+ 1. **Deferred Registration Validation (Resolved via GEP-032):** Parameter validation (`_validate_node_dependency_registration`) now runs during `GraphModel.compile()` rather than eagerly at decorator execution time, removing topological decoration-order constraints and enabling cross-model composition (`ModelA + ModelB`).
154
+ 2. **Immutable Model Compilation (Resolved via GEP-032):** `NetworkxBuilder` now compiles a clone of the `GraphModel` via `self.model.compile(...)` instead of mutating the shared `GraphModel` in-place via `rectify()`.
155
+ 3. **Recursive Lineage Namespace Collision (Resolved):** `_populate_nodes` and `_populate_node_type` now track an explicit `_lineage` tuple across recursion depth, preventing `folder -> folder` self-parenting key collisions when `unique=False`.
156
+ 4. **Lineage Casing Symmetry (Resolved):** `node_children_types`, `_validate_node_dependency_registration`, and `_parent_node_id` now resolve parent types case-insensitively.
157
+ 5. **Dead Code Cleanup (`NodeModel.parameters`) (Resolved):** Removed the obsolete `parameters` attribute from `NodeModel`.
157
158
 
158
159
  ## Copyright
159
160
 
@@ -5,9 +5,10 @@
5
5
  | **GEP** | 16 |
6
6
  | **Title** | CLI Argument Parsing & Path Safety |
7
7
  | **Author** | Eran Rivlis |
8
- | **Status** | Draft |
8
+ | **Status** | Active |
9
9
  | **Type** | Standards Track |
10
10
  | **Created** | 2025-12-25 |
11
+ | **Updated** | 2026-10-17 |
11
12
 
12
13
  ## Abstract
13
14
 
@@ -5,7 +5,7 @@
5
5
  | **GEP** | 17 |
6
6
  | **Title** | Decoupled Server Architecture |
7
7
  | **Author** | Eran Rivlis |
8
- | **Status** | Draft |
8
+ | **Status** | Active |
9
9
  | **Type** | Standards Track |
10
10
  | **Created** | 2025-12-25 |
11
11
 
@@ -28,13 +28,23 @@ This proposal suggests separating the app creation from the server execution.
28
28
  Refactor `renderers.graphql` to expose a factory function:
29
29
 
30
30
  ```python
31
- def create_app(schema: Schema, **kwargs) -> Starlette:
31
+ def create_app(
32
+ graphql_schema: strawberry.Schema,
33
+ port: int = DEFAULT_PORT,
34
+ debug: bool = False,
35
+ **kwargs: Any,
36
+ ) -> Starlette:
32
37
  # ... construct and return the app ...
33
38
  return app
34
39
 
35
40
 
36
- def server(schema: Schema, **kwargs):
37
- app = create_app(schema, **kwargs)
41
+ def server(
42
+ graphql_schema: strawberry.Schema,
43
+ port: int = DEFAULT_PORT,
44
+ debug: bool = False,
45
+ **kwargs: Any,
46
+ ) -> None:
47
+ app = create_app(graphql_schema, port=port, debug=debug, **kwargs)
38
48
  uvicorn.run(app, ...)
39
49
  ```
40
50
 
@@ -42,3 +52,13 @@ def server(schema: Schema, **kwargs):
42
52
 
43
53
  The `server` function signature will remain the same, ensuring backward compatibility. The new `create_app` function
44
54
  will be an additive change.
55
+
56
+ ## Reference Implementation
57
+
58
+ - [`src/graphinate/renderers/graphql.py`](file:///C:/dev/erivlis/graphinate/src/graphinate/renderers/graphql.py)
59
+ - [`tests/graphinate/renderers/test_graphql_server.py`](file:///C:/dev/erivlis/graphinate/tests/graphinate/renderers/test_graphql_server.py)
60
+
61
+ ## Change Log
62
+
63
+ - **2025-12-25:** Initial draft.
64
+ - **2026-10-17:** Implemented `create_app` in `graphinate.renderers.graphql` and refactored `server()` to delegate to `create_app`. Updated status to `Active`.
@@ -1,8 +1,14 @@
1
1
  # GEP-031: High-Performance Vectorized Operations
2
2
 
3
- * **Status:** Draft
4
- * **Type:** Standards Track
5
- * **Created:** 2023-10-27
3
+ | Field | Value |
4
+ |:------------|:---------------------------------------|
5
+ | **GEP** | 31 |
6
+ | **Title** | High-Performance Vectorized Operations |
7
+ | **Author** | Eran Rivlis |
8
+ | **Status** | Draft |
9
+ | **Type** | Standards Track |
10
+ | **Created** | 2023-10-27 |
11
+ | **Updated** | 2026-10-17 |
6
12
 
7
13
  ## Abstract
8
14
 
@@ -5,10 +5,10 @@
5
5
  | **GEP** | 32 |
6
6
  | **Title** | GraphModel Compilation and Validation Lifecycle |
7
7
  | **Author** | Eran Rivlis |
8
- | **Status** | Draft |
8
+ | **Status** | Active |
9
9
  | **Type** | Standards Track |
10
10
  | **Created** | 2026-06-22 |
11
- | **Updated** | 2026-06-22 |
11
+ | **Updated** | 2026-10-17 |
12
12
 
13
13
  ## Abstract
14
14
 
@@ -132,10 +132,12 @@ This proposal serves as the structural glue between several critical enhancement
132
132
  ## Reference Implementation
133
133
 
134
134
  * **Source File:** [modeling.py](file:///C:/dev/erivlis/graphinate/src/graphinate/modeling.py)
135
- * **Builder Integration:** [builder.py](file:///C:/dev/erivlis/graphinate/src/graphinate/builders/builder.py)
135
+ * **Builder Integration:** [networkx.py](file:///C:/dev/erivlis/graphinate/src/graphinate/builders/networkx.py)
136
136
 
137
137
  ---
138
138
 
139
139
  ## Change Log
140
140
 
141
141
  * 2026-06-22: Initial Draft outlining the compilation phases, late validation, and synergies with GEP-010, GEP-012, and GEP-024.
142
+ * 2026-10-17: Implemented `GraphModel.compile()`, `_validate_structural_dependencies()`, deferred dependency validation, and immutable model compilation in `NetworkxBuilder`.
143
+
@@ -0,0 +1,56 @@
1
+ ---
2
+ title: CLI
3
+ icon: lucide/command
4
+ ---
5
+ # CLI
6
+
7
+ ## Commands
8
+
9
+ ``` shell
10
+ Usage: python -m graphinate [OPTIONS] COMMAND [ARGS]...
11
+
12
+ Options:
13
+ --help Show this message and exit.
14
+
15
+ Commands:
16
+ save
17
+ server
18
+ ```
19
+
20
+ ### Save
21
+
22
+ ```console
23
+ Usage: python -m graphinate save [OPTIONS]
24
+
25
+ Options:
26
+ -m, --model MODEL A GraphModel instance reference {module-
27
+ name}:{GraphModel-instance-variable-name} For example,
28
+ given var `model=GraphModel()` defined in app.py file,
29
+ then the reference should be app:model
30
+ --debug / --no-debug Enable debug logging. [env var: GRAPHINATE_DEBUG]
31
+ --help Show this message and exit.
32
+ ```
33
+
34
+ ### Server
35
+
36
+ !!! tip
37
+
38
+ requires the `server` extra to be installed.
39
+
40
+ ```shell
41
+ pip install graphinate[server]
42
+ ```
43
+
44
+ ```console
45
+ Usage: python -m graphinate server [OPTIONS]
46
+
47
+ Options:
48
+ -m, --model MODEL A GraphModel instance reference {module-
49
+ name}:{GraphModel-instance-variable-name} For example,
50
+ given var `model=GraphModel()` defined in app.py file,
51
+ then the reference should be app:model
52
+ -p, --port INTEGER Port number.
53
+ -b, --browse / --no-browse Open web browser on start.
54
+ --debug / --no-debug Enable debug logging. [env var: GRAPHINATE_DEBUG]
55
+ --help Show this message and exit.
56
+ ```
@@ -21,7 +21,7 @@ icon: lucide/package
21
21
 
22
22
  The `GraphModel` Class which is used to declaratively register, Edge and/or Node data supplier functions.
23
23
  Using the [`GraphModel.node()`](../reference/graphinate/modeling.md#graphinate.modeling.GraphModel.node)
24
- and [`GraphMode.edge()`](../reference/graphinate/modeling.md#graphinate.modeling.GraphModel.edge) decorators.
24
+ and [`GraphModel.edge()`](../reference/graphinate/modeling.md#graphinate.modeling.GraphModel.edge) decorators.
25
25
 
26
26
  ### Builders
27
27
 
@@ -1,10 +1,12 @@
1
1
  [project]
2
2
  name = "Graphinate"
3
- version = "0.15.3"
3
+ version = "0.16.0"
4
4
  description = "Graphinate. Data to Graphs."
5
5
  authors = [
6
6
  { name = "Eran Rivlis", email = "eran@rivlis.info" },
7
7
  ]
8
+ license = "LGPL-3.0-or-later"
9
+ license-files = ["LICENSE"]
8
10
  readme = "README.md"
9
11
  requires-python = ">=3.11"
10
12
  classifiers = [
@@ -103,9 +103,9 @@ print(mermaid_string)
103
103
  ## 5. Gotchas & Best Practices
104
104
 
105
105
  - **The Command Center (`graphinate.cli`)**: For quick builds or serving, use the CLI:
106
- - `graphinate build --model <path/to/model.py>`
107
- - `graphinate serve --model <path/to/model.py>`
108
- - **The Broadcaster (`graphinate.server`)**: The `serve` command requires optional dependencies. If it fails, the user
106
+ - `graphinate save --model <module:model>`
107
+ - `graphinate server --model <module:model>`
108
+ - **The Broadcaster (`graphinate.server`)**: The `server` command requires optional dependencies. If it fails, the user
109
109
  needs to run:
110
110
  ```bash
111
111
  pip install "graphinate[server]"
@@ -28,6 +28,7 @@ from ..converters import (
28
28
  )
29
29
  from ..enums import GraphType
30
30
  from ..modeling import GraphModel
31
+ from ..tools import VariableNameManager
31
32
  from .networkx import NetworkxBuilder
32
33
 
33
34
 
@@ -173,7 +174,7 @@ class GraphQLBuilder(NetworkxBuilder):
173
174
  number_of_isolates = 'number_of_isolates'
174
175
  number_connected_components = 'number_connected_components'
175
176
  number_strongly_connected_components = 'number_strongly_connected_components'
176
- number_weakly_connected_components = ' number_weakly_connected_components'
177
+ number_weakly_connected_components = 'number_weakly_connected_components'
177
178
  number_attracting_components = 'number_attracting_components'
178
179
  node_connectivity = 'node_connectivity'
179
180
  transitivity = 'transitivity'
@@ -193,6 +194,7 @@ class GraphQLBuilder(NetworkxBuilder):
193
194
  def __init__(self, model: GraphModel, graph_type: GraphType = GraphType.Graph):
194
195
  super().__init__(model, graph_type)
195
196
  self._node_value_graphql_type_supplier: Callable[[str], StrawberryType | None] | None = None
197
+ self._cached_graphql_types: dict[str, type[GraphQLBuilder.GraphNode]] | None = None
196
198
 
197
199
  @staticmethod
198
200
  def add_field_resolver(class_dict: dict, field_name: str, resolver: Callable, graphql_type: Any | None = None):
@@ -248,8 +250,9 @@ class GraphQLBuilder(NetworkxBuilder):
248
250
 
249
251
  @staticmethod
250
252
  def _graphql_enum(name: str, values: list[str]) -> EnumType:
253
+ name_manager = VariableNameManager()
251
254
  return strawberry.enum(
252
- Enum(name, {v: v for v in values}),
255
+ Enum(name, {name_manager.make_valid(v): v for v in values}),
253
256
  name=name,
254
257
  description=f"{name} Enumeration"
255
258
  )
@@ -263,50 +266,56 @@ class GraphQLBuilder(NetworkxBuilder):
263
266
  from strawberry.types.enum import EnumValue
264
267
 
265
268
  types_to_populate = node_types if node_types else ['node']
269
+ name_manager = VariableNameManager()
270
+
271
+ self.GraphNodeType.__strawberry_definition__.values.clear()
266
272
 
267
273
  for v in types_to_populate:
268
- if v not in self.GraphNodeType._member_names_:
269
- self.GraphNodeType._member_names_.append(v)
270
- self.GraphNodeType._member_map_[v] = v
271
- self.GraphNodeType._value2member_map_[v] = v
272
-
273
- self.GraphNodeType.__strawberry_definition__.values.append(
274
- EnumValue(
275
- name=v,
276
- value=v,
277
- description=f"Graph Node Type: {v}"
278
- )
274
+ member_name = name_manager.make_valid(v)
275
+ if member_name not in self.GraphNodeType._member_names_:
276
+ self.GraphNodeType._member_names_.append(member_name)
277
+ self.GraphNodeType._member_map_[member_name] = v
278
+ self.GraphNodeType._value2member_map_[v] = v
279
+
280
+ self.GraphNodeType.__strawberry_definition__.values.append(
281
+ EnumValue(
282
+ name=member_name,
283
+ value=v,
284
+ description=f"Graph Node Type: {v}"
279
285
  )
280
-
286
+ )
281
287
 
282
288
  @property
283
- @functools.lru_cache
284
289
  def _graphql_types(self) -> dict[str, type['GraphQLBuilder.GraphNode']]:
290
+ if self._cached_graphql_types is not None:
291
+ return self._cached_graphql_types
292
+
285
293
  node_types = list(self._graph.graph['node_types'].keys())
286
294
 
287
295
  self._populate_graph_node_type_enum(node_types)
288
296
 
289
- def neighbors_resolver():
297
+ def neighbors_resolver(current_node_type: str):
290
298
  graph = self._graph
291
299
 
292
- children_types = set(self._children_types(self.model, node_type))
300
+ children_types = set(self._children_types(self.model, current_node_type))
293
301
 
294
302
  def node_neighbors(self,
295
303
  type: 'GraphQLBuilder.GraphNodeType | None' = None,
296
304
  children: bool = False) -> list['GraphQLBuilder.GraphNode']:
297
305
  node = decode_node_id(self.id)
298
- items = (GraphQLBuilder._graph_node(graphql_types[d['type']], n, d)
299
- for n, d in graph.nodes(data=True)
300
- if n in graph.neighbors(node))
306
+ items = (
307
+ GraphQLBuilder._graph_node(graphql_types[(d := graph.nodes[n])['type']], n, d)
308
+ for n in graph.neighbors(node)
309
+ )
301
310
 
302
311
  if type is not None:
303
- items = (item for item in items if item.type == type)
312
+ target_type = getattr(type, 'value', type)
313
+ items = (item for item in items if item.type == target_type)
304
314
 
305
315
  if children and children_types:
306
316
  items = (item for item in items if item.type in children_types)
307
317
 
308
- items = list(items)
309
- return items
318
+ return list(items)
310
319
 
311
320
  return node_neighbors
312
321
 
@@ -320,10 +329,11 @@ class GraphQLBuilder(NetworkxBuilder):
320
329
 
321
330
  return node_edges
322
331
 
332
+ class_name_manager = VariableNameManager()
323
333
  # Create classes for nodes according to their type
324
334
  graphql_types: dict[str, type[GraphQLBuilder.GraphNode]] = {}
325
335
  for node_type in node_types:
326
- class_name = node_type.capitalize()
336
+ class_name = class_name_manager.make_valid(node_type.capitalize())
327
337
  bases = (GraphQLBuilder.GraphNode,)
328
338
  class_dict = {
329
339
  '__doc__': f"A {class_name} Graph Node",
@@ -336,13 +346,14 @@ class GraphQLBuilder(NetworkxBuilder):
336
346
  ):
337
347
  class_dict['value'] = list[value_graphql_type]
338
348
 
339
- self.add_field_resolver(class_dict, 'neighbors', neighbors_resolver())
349
+ self.add_field_resolver(class_dict, 'neighbors', neighbors_resolver(node_type))
340
350
  self.add_field_resolver(class_dict, 'edges', edges_resolver())
341
351
 
342
352
  # noinspection PyTypeChecker
343
353
  graphql_type: type[GraphQLBuilder.GraphNode] = type(class_name, bases, class_dict)
344
354
  graphql_types[node_type] = GraphQLBuilder._graphql_type(node_type, graphql_type)
345
355
 
356
+ self._cached_graphql_types = graphql_types
346
357
  return graphql_types
347
358
 
348
359
  def _graphql_query(self): # noqa: C901
@@ -434,8 +445,9 @@ class GraphQLBuilder(NetworkxBuilder):
434
445
  # endregion
435
446
 
436
447
  # region - Defining GraphQL Query Class dict - fields for GraphQL types implementing 'GraphNode' interface
448
+ field_name_manager = VariableNameManager()
437
449
  for node_type, graphql_type in self._graphql_types.items():
438
- field_name = inflection.plural(node_type)
450
+ field_name = field_name_manager.make_valid(inflection.plural(node_type))
439
451
  resolver = graph_nodes_resolver(graphql_type, node_type)
440
452
  self.add_field_resolver(query_class_dict, field_name, resolver)
441
453
 
@@ -521,6 +533,7 @@ class GraphQLBuilder(NetworkxBuilder):
521
533
  Returns:
522
534
  Strawberry GraphQL Schema
523
535
  """
536
+ self._cached_graphql_types = None
524
537
  super().build(**kwargs)
525
538
 
526
539
  self._node_value_graphql_type_supplier = node_value_graphql_type_supplier