graphdatascience 1.15a1__tar.gz → 1.15a2__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 (163) hide show
  1. {graphdatascience-1.15a1/graphdatascience.egg-info → graphdatascience-1.15a2}/PKG-INFO +1 -1
  2. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/cypher_warning_handler.py +6 -0
  3. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph_data_science.py +8 -1
  4. graphdatascience-1.15a2/graphdatascience/query_runner/arrow_authentication.py +18 -0
  5. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/arrow_query_runner.py +3 -2
  6. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/gds_arrow_client.py +20 -14
  7. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/neo4j_query_runner.py +29 -39
  8. graphdatascience-1.15a2/graphdatascience/query_runner/standalone_session_query_runner.py +76 -0
  9. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/__init__.py +3 -0
  10. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/aura_api.py +61 -40
  11. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/aura_api_responses.py +43 -16
  12. graphdatascience-1.15a2/graphdatascience/session/aura_api_token_authentication.py +10 -0
  13. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/aura_graph_data_science.py +37 -28
  14. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/dedicated_sessions.py +78 -50
  15. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/gds_sessions.py +7 -7
  16. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/session_info.py +8 -4
  17. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/version.py +1 -1
  18. {graphdatascience-1.15a1 → graphdatascience-1.15a2/graphdatascience.egg-info}/PKG-INFO +1 -1
  19. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience.egg-info/SOURCES.txt +3 -0
  20. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/LICENSE +0 -0
  21. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/MANIFEST.in +0 -0
  22. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/README.md +0 -0
  23. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/__init__.py +0 -0
  24. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/algo/__init__.py +0 -0
  25. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/algo/algo_endpoints.py +0 -0
  26. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/algo/algo_proc_runner.py +0 -0
  27. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/algo/single_mode_algo_endpoints.py +0 -0
  28. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/call_builder.py +0 -0
  29. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/call_parameters.py +0 -0
  30. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/caller_base.py +0 -0
  31. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/endpoints.py +0 -0
  32. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/__init__.py +0 -0
  33. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/client_only_endpoint.py +0 -0
  34. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/endpoint_suggester.py +0 -0
  35. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/gds_not_installed.py +0 -0
  36. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/illegal_attr_checker.py +0 -0
  37. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/unable_to_connect.py +0 -0
  38. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/error/uncallable_namespace.py +0 -0
  39. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/__init__.py +0 -0
  40. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/base_graph_proc_runner.py +0 -0
  41. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_alpha_proc_runner.py +0 -0
  42. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_beta_proc_runner.py +0 -0
  43. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_create_result.py +0 -0
  44. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_cypher_runner.py +0 -0
  45. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_endpoints.py +0 -0
  46. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_entity_ops_runner.py +0 -0
  47. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_export_runner.py +0 -0
  48. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_object.py +0 -0
  49. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_proc_runner.py +0 -0
  50. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_project_runner.py +0 -0
  51. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_remote_proc_runner.py +0 -0
  52. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_remote_project_runner.py +0 -0
  53. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_sample_runner.py +0 -0
  54. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/graph_type_check.py +0 -0
  55. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/nx_loader.py +0 -0
  56. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/graph/ogb_loader.py +0 -0
  57. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/ignored_server_endpoints.py +0 -0
  58. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/__init__.py +0 -0
  59. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/graphsage_model.py +0 -0
  60. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/link_prediction_model.py +0 -0
  61. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model.py +0 -0
  62. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model_alpha_proc_runner.py +0 -0
  63. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model_beta_proc_runner.py +0 -0
  64. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model_endpoints.py +0 -0
  65. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model_proc_runner.py +0 -0
  66. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/model_resolver.py +0 -0
  67. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/node_classification_model.py +0 -0
  68. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/node_regression_model.py +0 -0
  69. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/pipeline_model.py +0 -0
  70. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/model/simple_rel_embedding_model.py +0 -0
  71. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/__init__.py +0 -0
  72. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/classification_training_pipeline.py +0 -0
  73. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/lp_pipeline_create_runner.py +0 -0
  74. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/lp_training_pipeline.py +0 -0
  75. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/nc_pipeline_create_runner.py +0 -0
  76. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/nc_training_pipeline.py +0 -0
  77. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/nr_pipeline_create_runner.py +0 -0
  78. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/nr_training_pipeline.py +0 -0
  79. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/pipeline_alpha_proc_runner.py +0 -0
  80. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/pipeline_beta_proc_runner.py +0 -0
  81. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/pipeline_endpoints.py +0 -0
  82. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/pipeline_proc_runner.py +0 -0
  83. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/pipeline/training_pipeline.py +0 -0
  84. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/py.typed +0 -0
  85. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/__init__.py +0 -0
  86. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/arrow_endpoint_version.py +0 -0
  87. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/arrow_graph_constructor.py +0 -0
  88. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/arrow_info.py +0 -0
  89. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/cypher_graph_constructor.py +0 -0
  90. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/graph_constructor.py +0 -0
  91. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/progress/__init__.py +0 -0
  92. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/progress/progress_provider.py +0 -0
  93. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/progress/query_progress_logger.py +0 -0
  94. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/progress/query_progress_provider.py +0 -0
  95. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/progress/static_progress_provider.py +0 -0
  96. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/protocol/__init__.py +0 -0
  97. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/protocol/project_protocols.py +0 -0
  98. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/protocol/status.py +0 -0
  99. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/protocol/write_protocols.py +0 -0
  100. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/query_runner.py +0 -0
  101. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/session_query_runner.py +0 -0
  102. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/query_runner/termination_flag.py +0 -0
  103. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/__init__.py +0 -0
  104. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/cora/__init__.py +0 -0
  105. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/cora/cora_nodes.parquet.gzip +0 -0
  106. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/cora/cora_rels.parquet.gzip +0 -0
  107. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/cora/serialize_cora.py +0 -0
  108. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/__init__.py +0 -0
  109. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_acted_in.parquet.gzip +0 -0
  110. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_actors.parquet.gzip +0 -0
  111. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_directed_in.parquet.gzip +0 -0
  112. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_directors.parquet.gzip +0 -0
  113. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_movies_with_genre.parquet.gzip +0 -0
  114. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/imdb_movies_without_genre.parquet.gzip +0 -0
  115. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/imdb/serialize_imdb.py +0 -0
  116. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/karate/__init__.py +0 -0
  117. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/karate/karate_club.parquet.gzip +0 -0
  118. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/__init__.py +0 -0
  119. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/artist_nodes.parquet.gzip +0 -0
  120. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/serialize_lastfm.py +0 -0
  121. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/user_friend_df_directed.parquet.gzip +0 -0
  122. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/user_listen_artist_rels.parquet.gzip +0 -0
  123. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/user_nodes.parquet.gzip +0 -0
  124. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/resources/lastfm/user_tag_artist_rels.parquet.gzip +0 -0
  125. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/retry_utils/__init__.py +0 -0
  126. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/retry_utils/retry_config.py +0 -0
  127. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/retry_utils/retry_utils.py +0 -0
  128. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/semantic_version/__init__.py +0 -0
  129. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/semantic_version/semantic_version.py +0 -0
  130. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/server_version/__init__.py +0 -0
  131. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/server_version/compatible_with.py +0 -0
  132. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/server_version/server_version.py +0 -0
  133. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/algorithm_category.py +0 -0
  134. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/aurads_sessions.py +0 -0
  135. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/cloud_location.py +0 -0
  136. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/dbms/__init__.py +0 -0
  137. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/dbms/protocol_resolver.py +0 -0
  138. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/dbms/protocol_version.py +0 -0
  139. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/dbms_connection_info.py +0 -0
  140. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/region_suggester.py +0 -0
  141. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/session/session_sizes.py +0 -0
  142. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/system/__init__.py +0 -0
  143. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/system/config_endpoints.py +0 -0
  144. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/system/system_endpoints.py +0 -0
  145. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/topological_lp/__init__.py +0 -0
  146. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/topological_lp/topological_lp_alpha_runner.py +0 -0
  147. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/topological_lp/topological_lp_endpoints.py +0 -0
  148. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/utils/__init__.py +0 -0
  149. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/utils/direct_util_endpoints.py +0 -0
  150. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/utils/util_node_property_func_runner.py +0 -0
  151. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/utils/util_proc_runner.py +0 -0
  152. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience/utils/util_remote_proc_runner.py +0 -0
  153. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience.egg-info/dependency_links.txt +0 -0
  154. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience.egg-info/not-zip-safe +0 -0
  155. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience.egg-info/requires.txt +0 -0
  156. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/graphdatascience.egg-info/top_level.txt +0 -0
  157. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/pyproject.toml +0 -0
  158. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/requirements/base/base.txt +0 -0
  159. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/requirements/base/networkx.txt +0 -0
  160. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/requirements/base/ogb.txt +0 -0
  161. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/requirements/base/rust-ext.txt +0 -0
  162. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/setup.cfg +0 -0
  163. {graphdatascience-1.15a1 → graphdatascience-1.15a2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphdatascience
3
- Version: 1.15a1
3
+ Version: 1.15a2
4
4
  Summary: A Python client for the Neo4j Graph Data Science (GDS) library
5
5
  Home-page: https://neo4j.com/product/graph-data-science/
6
6
  Author: Neo4j
@@ -24,6 +24,12 @@ def filter_id_func_deprecation_warning() -> Callable[[F], F]:
24
24
  message=r"^The query used a deprecated function. \('id' is no longer supported\)",
25
25
  )
26
26
 
27
+ # since 2025.04.0
28
+ warnings.filterwarnings(
29
+ "ignore",
30
+ message=r"^The query used a deprecated function. \('id' has been replaced by 'elementId or an application-generated id'\)",
31
+ )
32
+
27
33
  return func(self, *args, **kwargs)
28
34
 
29
35
  return cast(F, wrapper)
@@ -7,6 +7,8 @@ from typing import Any, Optional, Type, Union
7
7
  from neo4j import Driver
8
8
  from pandas import DataFrame
9
9
 
10
+ from graphdatascience.query_runner.arrow_authentication import UsernamePasswordAuthentication
11
+
10
12
  from .call_builder import IndirectCallBuilder
11
13
  from .endpoints import AlphaEndpoints, BetaEndpoints, DirectEndpoints
12
14
  from .error.uncallable_namespace import UncallableNamespace
@@ -93,10 +95,15 @@ class GraphDataScience(DirectEndpoints, UncallableNamespace):
93
95
 
94
96
  arrow_info = ArrowInfo.create(self._query_runner)
95
97
  if arrow and arrow_info.enabled and self._server_version >= ServerVersion(2, 1, 0):
98
+ arrow_auth = None
99
+ if auth is not None:
100
+ username, password = auth
101
+ arrow_auth = UsernamePasswordAuthentication(username, password)
102
+
96
103
  self._query_runner = ArrowQueryRunner.create(
97
104
  self._query_runner,
98
105
  arrow_info,
99
- auth,
106
+ arrow_auth,
100
107
  self._query_runner.encrypted(),
101
108
  arrow_disable_server_verification,
102
109
  arrow_tls_root_certs,
@@ -0,0 +1,18 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+
4
+
5
+ class ArrowAuthentication(ABC):
6
+ @abstractmethod
7
+ def auth_pair(self) -> tuple[str, str]:
8
+ """Returns the auth pair used for authentication."""
9
+ pass
10
+
11
+
12
+ @dataclass
13
+ class UsernamePasswordAuthentication(ArrowAuthentication):
14
+ username: str
15
+ password: str
16
+
17
+ def auth_pair(self) -> tuple[str, str]:
18
+ return self.username, self.password
@@ -5,6 +5,7 @@ from typing import Any, Optional
5
5
 
6
6
  from pandas import DataFrame
7
7
 
8
+ from graphdatascience.query_runner.arrow_authentication import ArrowAuthentication
8
9
  from graphdatascience.retry_utils.retry_config import RetryConfig
9
10
 
10
11
  from ..call_parameters import CallParameters
@@ -21,7 +22,7 @@ class ArrowQueryRunner(QueryRunner):
21
22
  def create(
22
23
  fallback_query_runner: QueryRunner,
23
24
  arrow_info: ArrowInfo,
24
- auth: Optional[tuple[str, str]] = None,
25
+ arrow_authentication: Optional[ArrowAuthentication] = None,
25
26
  encrypted: bool = False,
26
27
  disable_server_verification: bool = False,
27
28
  tls_root_certs: Optional[bytes] = None,
@@ -33,7 +34,7 @@ class ArrowQueryRunner(QueryRunner):
33
34
 
34
35
  gds_arrow_client = GdsArrowClient.create(
35
36
  arrow_info,
36
- auth,
37
+ arrow_authentication,
37
38
  encrypted,
38
39
  disable_server_verification,
39
40
  tls_root_certs,
@@ -35,6 +35,7 @@ from tenacity import (
35
35
  wait_exponential,
36
36
  )
37
37
 
38
+ from graphdatascience.query_runner.arrow_authentication import ArrowAuthentication, UsernamePasswordAuthentication
38
39
  from graphdatascience.retry_utils.retry_config import RetryConfig
39
40
  from graphdatascience.retry_utils.retry_utils import before_log
40
41
 
@@ -48,7 +49,7 @@ class GdsArrowClient:
48
49
  @staticmethod
49
50
  def create(
50
51
  arrow_info: ArrowInfo,
51
- auth: Optional[tuple[str, str]] = None,
52
+ auth: Optional[Union[ArrowAuthentication, tuple[str, str]]] = None,
52
53
  encrypted: bool = False,
53
54
  disable_server_verification: bool = False,
54
55
  tls_root_certs: Optional[bytes] = None,
@@ -92,7 +93,7 @@ class GdsArrowClient:
92
93
  host: str,
93
94
  retry_config: RetryConfig,
94
95
  port: int = 8491,
95
- auth: Optional[tuple[str, str]] = None,
96
+ auth: Optional[Union[ArrowAuthentication, tuple[str, str]]] = None,
96
97
  encrypted: bool = False,
97
98
  disable_server_verification: bool = False,
98
99
  tls_root_certs: Optional[bytes] = None,
@@ -107,8 +108,8 @@ class GdsArrowClient:
107
108
  The host address of the GDS Arrow server
108
109
  port: int
109
110
  The host port of the GDS Arrow server (default is 8491)
110
- auth: Optional[tuple[str, str]]
111
- A tuple containing the username and password for authentication
111
+ auth: Optional[Union[ArrowAuthentication, tuple[str, str]]]
112
+ Either an implementation of ArrowAuthentication providing a pair to be used for basic authentication, or a username, password tuple
112
113
  encrypted: bool
113
114
  A flag that indicates whether the connection should be encrypted (default is False)
114
115
  disable_server_verification: bool
@@ -125,7 +126,7 @@ class GdsArrowClient:
125
126
  self._arrow_endpoint_version = arrow_endpoint_version
126
127
  self._host = host
127
128
  self._port = port
128
- self._auth = auth
129
+ self._auth = None
129
130
  self._encrypted = encrypted
130
131
  self._disable_server_verification = disable_server_verification
131
132
  self._tls_root_certs = tls_root_certs
@@ -134,6 +135,10 @@ class GdsArrowClient:
134
135
  self._logger = logging.getLogger("gds_arrow_client")
135
136
 
136
137
  if auth:
138
+ if not isinstance(auth, ArrowAuthentication):
139
+ username, password = auth
140
+ auth = UsernamePasswordAuthentication(username, password)
141
+ self._auth = auth
137
142
  self._auth_middleware = AuthMiddleware(auth)
138
143
 
139
144
  self._flight_client = self._instantiate_flight_client()
@@ -189,7 +194,8 @@ class GdsArrowClient:
189
194
  def auth_with_retry() -> None:
190
195
  client = self._client()
191
196
  if self._auth:
192
- client.authenticate_basic_token(self._auth[0], self._auth[1])
197
+ auth_pair = self._auth.auth_pair()
198
+ client.authenticate_basic_token(auth_pair[0], auth_pair[1])
193
199
 
194
200
  if self._auth:
195
201
  auth_with_retry()
@@ -884,7 +890,7 @@ class AuthFactory(ClientMiddlewareFactory): # type: ignore
884
890
 
885
891
 
886
892
  class AuthMiddleware(ClientMiddleware): # type: ignore
887
- def __init__(self, auth: tuple[str, str], *args: Any, **kwargs: Any) -> None:
893
+ def __init__(self, auth: ArrowAuthentication, *args: Any, **kwargs: Any) -> None:
888
894
  super().__init__(*args, **kwargs)
889
895
  self._auth = auth
890
896
  self._token: Optional[str] = None
@@ -918,15 +924,15 @@ class AuthMiddleware(ClientMiddleware): # type: ignore
918
924
 
919
925
  def sending_headers(self) -> dict[str, str]:
920
926
  token = self.token()
921
- if not token:
922
- username, password = self._auth
923
- auth_token = f"{username}:{password}"
924
- auth_token = "Basic " + base64.b64encode(auth_token.encode("utf-8")).decode("ASCII")
925
- # There seems to be a bug, `authorization` must be lower key
926
- return {"authorization": auth_token}
927
- else:
927
+ if token is not None:
928
928
  return {"authorization": "Bearer " + token}
929
929
 
930
+ auth_pair = self._auth.auth_pair()
931
+ auth_token = f"{auth_pair[0]}:{auth_pair[1]}"
932
+ auth_token = "Basic " + base64.b64encode(auth_token.encode("utf-8")).decode("ASCII")
933
+ # There seems to be a bug, `authorization` must be lower key
934
+ return {"authorization": auth_token}
935
+
930
936
 
931
937
  @dataclass(repr=True, frozen=True)
932
938
  class NodeLoadDoneResult:
@@ -57,19 +57,9 @@ class Neo4jQueryRunner(QueryRunner):
57
57
  query_runner = Neo4jQueryRunner(
58
58
  endpoint, auto_close=False, bookmarks=bookmarks, database=database, show_progress=show_progress
59
59
  )
60
-
61
60
  else:
62
61
  raise ValueError(f"Invalid endpoint type: {type(endpoint)}")
63
62
 
64
- if Neo4jQueryRunner._NEO4J_DRIVER_VERSION >= SemanticVersion(5, 21, 0):
65
- notifications_logger = logging.getLogger("neo4j.notifications")
66
- # the client does not expose YIELD fields so we just skip these warnings for now
67
- notifications_logger.addFilter(
68
- lambda record: (
69
- "The query used a deprecated field from a procedure" in record.msg and "by 'gds." in record.msg
70
- )
71
- )
72
-
73
63
  return query_runner
74
64
 
75
65
  @staticmethod
@@ -90,19 +80,12 @@ class Neo4jQueryRunner(QueryRunner):
90
80
  show_progress=show_progress,
91
81
  bookmarks=None,
92
82
  config=driver_config,
83
+ # we need to explicitly set this as the default value is None
84
+ # database in the session is always neo4j
93
85
  database="neo4j",
94
86
  instance_description="GDS Session",
95
87
  )
96
88
 
97
- if Neo4jQueryRunner._NEO4J_DRIVER_VERSION >= SemanticVersion(5, 21, 0):
98
- notifications_logger = logging.getLogger("neo4j.notifications")
99
- # the client does not expose YIELD fields so we just skip these warnings for now
100
- notifications_logger.addFilter(
101
- lambda record: (
102
- "The query used a deprecated field from a procedure" in record.msg and "by 'gds." in record.msg
103
- )
104
- )
105
-
106
89
  return query_runner
107
90
 
108
91
  @staticmethod
@@ -167,12 +150,7 @@ class Neo4jQueryRunner(QueryRunner):
167
150
  else:
168
151
  raise e
169
152
 
170
- # Though pandas support may be experimental in the `neo4j` package, it should always
171
- # be supported in the `graphdatascience` package.
172
- warnings.filterwarnings(
173
- "ignore",
174
- message=r"^pandas support is experimental and might be changed or removed in future versions$",
175
- )
153
+ self.__configure_warnings_filter()
176
154
 
177
155
  df = result.to_df()
178
156
 
@@ -181,19 +159,10 @@ class Neo4jQueryRunner(QueryRunner):
181
159
  else:
182
160
  self._last_bookmarks = session.last_bookmarks()
183
161
 
184
- if (
185
- Neo4jQueryRunner._NEO4J_DRIVER_VERSION >= SemanticVersion(5, 21, 0)
186
- and result._warn_notification_severity == "WARNING"
187
- ):
188
- # the client does not expose YIELD fields so we just skip these warnings for now
189
- warnings.filterwarnings(
190
- "ignore", message=r".*The query used a deprecated field from a procedure\. .* by 'gds.* "
191
- )
192
- else:
193
- notifications = result.consume().notifications
194
- if notifications:
195
- for notification in notifications:
196
- self._forward_cypher_warnings(notification)
162
+ notifications = result.consume().notifications
163
+ if notifications:
164
+ for notification in notifications:
165
+ self._forward_cypher_warnings(notification)
197
166
 
198
167
  return df
199
168
 
@@ -266,7 +235,7 @@ class Neo4jQueryRunner(QueryRunner):
266
235
  # (see https://neo4j.com/docs/status-codes/current/notifications/ for more details)
267
236
  severity = notification["severity"]
268
237
  if severity == "WARNING":
269
- if "query used a deprecated field from a procedure" in notification["description"]:
238
+ if "deprecated field" in notification["description"] and "procedure" in notification["description"]:
270
239
  # the client does not expose YIELD fields so we just skip these warnings for now
271
240
  return
272
241
 
@@ -373,6 +342,27 @@ class Neo4jQueryRunner(QueryRunner):
373
342
  if retrys == retry_config.max_retries:
374
343
  raise UnableToConnectError(f"Unable to connect to the {self._instance_description}") from exception
375
344
 
345
+ def __configure_warnings_filter(self) -> None:
346
+ if Neo4jQueryRunner._NEO4J_DRIVER_VERSION >= SemanticVersion(5, 21, 0):
347
+ notifications_logger = logging.getLogger("neo4j.notifications")
348
+ # the client does not expose YIELD fields so we just skip these warnings for now
349
+ notifications_logger.addFilter(
350
+ lambda record: (
351
+ "The query used a deprecated field from a procedure" in record.msg and "by 'gds." in record.msg
352
+ )
353
+ )
354
+ notifications_logger.addFilter(
355
+ lambda record: "The procedure has a deprecated field" in record.msg and "gds." in record.msg
356
+ )
357
+ warnings.filterwarnings(
358
+ "ignore",
359
+ message=r"^pandas support is experimental and might be changed or removed in future versions$",
360
+ )
361
+ # neo4j 2025.04
362
+ warnings.filterwarnings("ignore", message=r".*The procedure has a deprecated field.*by 'gds.*")
363
+ # neo4j driver 4.4
364
+ warnings.filterwarnings("ignore", message=r".*The query used a deprecated field from a procedure.*by 'gds.*")
365
+
376
366
  class ConnectivityRetriesConfig(NamedTuple):
377
367
  max_retries: int = 600
378
368
  wait_time: int = 1
@@ -0,0 +1,76 @@
1
+ from typing import Any, Optional
2
+
3
+ from pandas import DataFrame
4
+
5
+ from graphdatascience import QueryRunner, ServerVersion
6
+ from graphdatascience.call_parameters import CallParameters
7
+ from graphdatascience.query_runner.graph_constructor import GraphConstructor
8
+
9
+
10
+ class StandaloneSessionQueryRunner(QueryRunner):
11
+ def __init__(self, session_query_runner: QueryRunner):
12
+ self._query_runner = session_query_runner
13
+
14
+ def call_procedure(
15
+ self,
16
+ endpoint: str,
17
+ params: Optional[CallParameters] = None,
18
+ yields: Optional[list[str]] = None,
19
+ database: Optional[str] = None,
20
+ logging: bool = False,
21
+ custom_error: bool = True,
22
+ ) -> DataFrame:
23
+ if endpoint.endswith(".write"):
24
+ raise NotImplementedError("write procedures are not supported on standalone sessions")
25
+
26
+ return self._query_runner.call_procedure(endpoint, params, yields, database, logging, custom_error)
27
+
28
+ def call_function(self, endpoint: str, params: Optional[CallParameters] = None) -> Any:
29
+ return self._query_runner.call_function(endpoint, params)
30
+
31
+ def server_version(self) -> ServerVersion:
32
+ return self._query_runner.server_version()
33
+
34
+ def create_graph_constructor(
35
+ self, graph_name: str, concurrency: int, undirected_relationship_types: Optional[list[str]]
36
+ ) -> GraphConstructor:
37
+ return self._query_runner.create_graph_constructor(graph_name, concurrency, undirected_relationship_types)
38
+
39
+ def set_show_progress(self, show_progress: bool) -> None:
40
+ self._query_runner.set_show_progress(show_progress)
41
+
42
+ def encrypted(self) -> bool:
43
+ return self._query_runner.encrypted()
44
+
45
+ def close(self) -> None:
46
+ self._query_runner.close()
47
+
48
+ def database(self) -> Optional[str]:
49
+ return "neo4j"
50
+
51
+ def run_cypher(
52
+ self,
53
+ query: str,
54
+ params: Optional[dict[str, Any]] = None,
55
+ database: Optional[str] = None,
56
+ custom_error: bool = True,
57
+ ) -> DataFrame:
58
+ raise NotImplementedError
59
+
60
+ def driver_config(self) -> dict[str, Any]:
61
+ raise NotImplementedError
62
+
63
+ def set_database(self, database: str) -> None:
64
+ raise NotImplementedError
65
+
66
+ def set_bookmarks(self, bookmarks: Optional[Any]) -> None:
67
+ raise NotImplementedError
68
+
69
+ def bookmarks(self) -> Optional[Any]:
70
+ raise NotImplementedError
71
+
72
+ def last_bookmarks(self) -> Optional[Any]:
73
+ raise NotImplementedError
74
+
75
+ def set_server_version(self, _: ServerVersion) -> None:
76
+ super().set_server_version(_)
@@ -1,4 +1,5 @@
1
1
  from .algorithm_category import AlgorithmCategory
2
+ from .aura_api import AuraApiError, SessionStatusError
2
3
  from .cloud_location import CloudLocation
3
4
  from .dbms_connection_info import DbmsConnectionInfo
4
5
  from .gds_sessions import AuraAPICredentials, GdsSessions
@@ -14,4 +15,6 @@ __all__ = [
14
15
  "SessionMemory",
15
16
  "SessionMemoryValue",
16
17
  "AlgorithmCategory",
18
+ "SessionStatusError",
19
+ "AuraApiError",
17
20
  ]
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import logging
4
4
  import math
5
5
  import time
6
- import uuid
7
6
  import warnings
8
7
  from collections import defaultdict
9
8
  from datetime import timedelta
@@ -22,8 +21,10 @@ from graphdatascience.session.aura_api_responses import (
22
21
  InstanceCreateDetails,
23
22
  InstanceDetails,
24
23
  InstanceSpecificDetails,
24
+ ProjectDetails,
25
25
  SessionDetails,
26
- TenantDetails,
26
+ SessionDetailsWithErrors,
27
+ SessionErrorData,
27
28
  WaitResult,
28
29
  )
29
30
  from graphdatascience.session.cloud_location import CloudLocation
@@ -32,33 +33,51 @@ from graphdatascience.version import __version__
32
33
 
33
34
 
34
35
  class AuraApiError(Exception):
36
+ """
37
+ Raised when an API call to the AuraAPI fails (after retries).
38
+ """
39
+
35
40
  def __init__(self, message: str, status_code: int):
36
41
  super().__init__(self, message)
37
42
  self.status_code = status_code
38
43
  self.message = message
39
44
 
40
45
 
46
+ class SessionStatusError(Exception):
47
+ """
48
+ Raised when a session is in a non-healthy state. Such as after a session failed or got expired.
49
+ """
50
+
51
+ def __init__(self, errors: list[SessionErrorData]):
52
+ message = f"Session is in an unhealthy state. Details: {[str(e) for e in errors]}"
53
+
54
+ super().__init__(self, message)
55
+
56
+
41
57
  class AuraApi:
42
- API_VERSION = "v1beta5"
58
+ API_VERSION = "v1"
43
59
 
44
60
  def __init__(
45
- self, client_id: str, client_secret: str, tenant_id: Optional[str] = None, aura_env: Optional[str] = None
61
+ self, client_id: str, client_secret: str, project_id: Optional[str] = None, aura_env: Optional[str] = None
46
62
  ) -> None:
47
63
  self._base_uri = AuraApi.base_uri(aura_env)
48
64
  self._credentials = (client_id, client_secret)
49
65
 
50
- self._request_session = self._init_request_session(self._credentials)
66
+ self._auth = AuraApi.Auth(
67
+ oauth_url=f"{self._base_uri}/oauth/token",
68
+ credentials=self._credentials,
69
+ headers={"User-agent": f"neo4j-graphdatascience-v{__version__}"},
70
+ )
71
+ self._request_session = self._init_request_session()
51
72
  self._logger = logging.getLogger()
52
73
 
53
- self._tenant_id = tenant_id if tenant_id else self._get_tenant_id()
54
- self._tenant_details: Optional[TenantDetails] = None
74
+ self._project_id = project_id if project_id else self._get_project_id()
75
+ self._project_details: Optional[ProjectDetails] = None
55
76
 
56
- def _init_request_session(self, credentials: tuple[str, str]) -> requests.Session:
77
+ def _init_request_session(self) -> requests.Session:
57
78
  request_session = requests.Session()
58
79
  request_session.headers = {"User-agent": f"neo4j-graphdatascience-v{__version__}"}
59
- request_session.auth = AuraApi.Auth(
60
- oauth_url=f"{self._base_uri}/oauth/token", credentials=credentials, headers=request_session.headers
61
- )
80
+ request_session.auth = self._auth
62
81
  # dont retry on POST as its not idempotent
63
82
  request_session.mount(
64
83
  "https://",
@@ -106,8 +125,7 @@ class AuraApi:
106
125
  ttl: Optional[timedelta] = None,
107
126
  cloud_location: Optional[CloudLocation] = None,
108
127
  ) -> SessionDetails:
109
- pwd = str(uuid.uuid4()) # password wont be used and will go away in v1 endpoints
110
- json = {"name": name, "password": pwd, "memory": memory.value, "tenant_id": self._tenant_id}
128
+ json = {"name": name, "memory": memory.value, "project_id": self._project_id}
111
129
 
112
130
  if dbid:
113
131
  json["instance_id"] = dbid
@@ -120,17 +138,19 @@ class AuraApi:
120
138
  json["region"] = cloud_location.region
121
139
 
122
140
  response = self._request_session.post(
123
- f"{self._base_uri}/{AuraApi.API_VERSION}/data-science/sessions", json=json
141
+ f"{self._base_uri}/{AuraApi.API_VERSION}/graph-analytics/sessions", json=json
124
142
  )
125
143
 
126
144
  self._check_resp(response)
127
145
 
128
146
  raw_json: dict[str, Any] = response.json()
129
- return SessionDetails.from_json(raw_json["data"], raw_json.get("errors", []))
147
+ self._check_errors(raw_json)
148
+
149
+ return SessionDetails.from_json(raw_json["data"])
130
150
 
131
151
  def get_session(self, session_id: str) -> Optional[SessionDetails]:
132
152
  response = self._request_session.get(
133
- f"{self._base_uri}/{AuraApi.API_VERSION}/data-science/sessions/{session_id}"
153
+ f"{self._base_uri}/{AuraApi.API_VERSION}/graph-analytics/sessions/{session_id}"
134
154
  )
135
155
 
136
156
  if response.status_code == HTTPStatus.NOT_FOUND.value:
@@ -139,17 +159,19 @@ class AuraApi:
139
159
  self._check_resp(response)
140
160
 
141
161
  raw_json: dict[str, Any] = response.json()
142
- return SessionDetails.from_json(raw_json["data"], raw_json.get("errors", []))
162
+ self._check_errors(raw_json)
143
163
 
144
- def list_sessions(self, dbid: Optional[str] = None) -> list[SessionDetails]:
164
+ return SessionDetails.from_json(raw_json["data"])
165
+
166
+ def list_sessions(self, dbid: Optional[str] = None) -> list[SessionDetailsWithErrors]:
145
167
  # these are query parameters (not passed in the body)
146
168
  params = {
147
- "tenantId": self._tenant_id,
169
+ "projectId": self._project_id,
148
170
  "instanceId": dbid,
149
171
  }
150
172
 
151
173
  response = self._request_session.get(
152
- f"{self._base_uri}/{AuraApi.API_VERSION}/data-science/sessions", params=params
174
+ f"{self._base_uri}/{AuraApi.API_VERSION}/graph-analytics/sessions", params=params
153
175
  )
154
176
 
155
177
  self._check_resp(response)
@@ -161,7 +183,7 @@ class AuraApi:
161
183
  for error in raw_json.get("errors", []):
162
184
  errors_per_session[error["id"]].append(error)
163
185
 
164
- return [SessionDetails.from_json(s, errors_per_session[s["id"]]) for s in data]
186
+ return [SessionDetailsWithErrors.from_json_with_error(s, errors_per_session[s["id"]]) for s in data] # noqa: F821
165
187
 
166
188
  def wait_for_session_running(
167
189
  self,
@@ -177,14 +199,6 @@ class AuraApi:
177
199
  return WaitResult.from_error(f"Session `{session_id}` not found -- please retry")
178
200
  elif session.status == "Ready":
179
201
  return WaitResult.from_connection_url(session.bolt_connection_url())
180
- elif session.status == "Failed":
181
- return WaitResult.from_error(
182
- f"Session `{session_id}` with name `{session.name}` failed due to: {session.errors}"
183
- )
184
- elif session.is_expired():
185
- return WaitResult.from_error(
186
- f"Session `{session_id}` with name `{session.name}` is expired. Expired due to: {session.errors}"
187
- )
188
202
  else:
189
203
  self._logger.debug(
190
204
  f"Session `{session_id}` is not yet running. "
@@ -204,7 +218,7 @@ class AuraApi:
204
218
 
205
219
  def delete_session(self, session_id: str) -> bool:
206
220
  response = self._request_session.delete(
207
- f"{self._base_uri}/{AuraApi.API_VERSION}/data-science/sessions/{session_id}",
221
+ f"{self._base_uri}/{AuraApi.API_VERSION}/graph-analytics/sessions/{session_id}",
208
222
  )
209
223
  self._check_endpoint_deprecation(response)
210
224
 
@@ -223,7 +237,7 @@ class AuraApi:
223
237
  "version": "5",
224
238
  "region": region,
225
239
  "type": type,
226
- "tenant_id": self._tenant_id,
240
+ "tenant_id": self._project_id,
227
241
  "cloud_provider": cloud_provider,
228
242
  }
229
243
 
@@ -244,7 +258,7 @@ class AuraApi:
244
258
  return InstanceSpecificDetails.fromJson(response.json()["data"])
245
259
 
246
260
  def list_instances(self) -> list[InstanceDetails]:
247
- response = self._request_session.get(f"{self._base_uri}/v1/instances", params={"tenantId": self._tenant_id})
261
+ response = self._request_session.get(f"{self._base_uri}/v1/instances", params={"tenantId": self._project_id})
248
262
 
249
263
  self._check_resp(response)
250
264
 
@@ -303,26 +317,33 @@ class AuraApi:
303
317
 
304
318
  return EstimationDetails.from_json(response.json()["data"])
305
319
 
306
- def _get_tenant_id(self) -> str:
320
+ def _get_project_id(self) -> str:
307
321
  response = self._request_session.get(f"{self._base_uri}/v1/tenants")
308
322
  self._check_resp(response)
309
323
 
310
324
  raw_data = response.json()["data"]
311
325
 
312
326
  if len(raw_data) != 1:
313
- tenants_dict = {d["id"]: d["name"] for d in raw_data}
327
+ projects_dict = {d["id"]: d["name"] for d in raw_data}
314
328
  raise RuntimeError(
315
- f"This account has access to multiple tenants: `{tenants_dict}`. Please specify which one to use."
329
+ f"This account has access to multiple projects: `{projects_dict}`. Please specify which one to use."
316
330
  )
317
331
 
318
332
  return raw_data[0]["id"] # type: ignore
319
333
 
320
- def tenant_details(self) -> TenantDetails:
321
- if not self._tenant_details:
322
- response = self._request_session.get(f"{self._base_uri}/v1/tenants/{self._tenant_id}")
334
+ def project_details(self) -> ProjectDetails:
335
+ if not self._project_details:
336
+ response = self._request_session.get(f"{self._base_uri}/v1/tenants/{self._project_id}")
323
337
  self._check_resp(response)
324
- self._tenant_details = TenantDetails.from_json(response.json()["data"])
325
- return self._tenant_details
338
+ self._project_details = ProjectDetails.from_json(response.json()["data"])
339
+ return self._project_details
340
+
341
+ def _check_errors(self, raw_json: dict[str, Any]) -> None:
342
+ errors = raw_json.get("errors", [])
343
+ typed_errors = [SessionErrorData.from_json(error) for error in errors] if errors else None
344
+
345
+ if typed_errors:
346
+ raise SessionStatusError(typed_errors)
326
347
 
327
348
  def _check_resp(self, resp: requests.Response) -> None:
328
349
  self._check_status_code(resp)