diff --git a/terminusdb_client/tests/integration_tests/conftest.py b/terminusdb_client/tests/integration_tests/conftest.py index c5e5d6e6..4a70bfed 100644 --- a/terminusdb_client/tests/integration_tests/conftest.py +++ b/terminusdb_client/tests/integration_tests/conftest.py @@ -13,7 +13,8 @@ def is_local_server_running(): """Check if local TerminusDB server is running at http://127.0.0.1:6363""" try: response = requests.get("http://127.0.0.1:6363", timeout=2) - # Server responds with 404 for root path, which means it's running + # Server responds with 200 (success) or 404 (not found but server is up) + # 401 (unauthorized) also indicates server is running but needs auth return response.status_code in [200, 404] except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): return False @@ -73,7 +74,9 @@ def docker_url_jwt(pytestconfig): # Check if JWT server is already running (port 6367) if is_jwt_server_running(): - print("\n✓ Using existing JWT Docker TerminusDB server at http://127.0.0.1:6367") + print( + "\n✓ Using existing JWT Docker TerminusDB server at http://127.0.0.1:6367" + ) yield ("http://127.0.0.1:6367", jwt_token) return # Don't clean up - server was already running @@ -128,7 +131,9 @@ def docker_url_jwt(pytestconfig): if seconds_waited > MAX_CONTAINER_STARTUP_TIME: clean_up_container() - raise RuntimeError(f"JWT Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)") + raise RuntimeError( + f"JWT Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)" + ) yield (test_url, jwt_token) clean_up_container() @@ -145,9 +150,15 @@ def docker_url(pytestconfig): """ # Check if local test server is already running (port 6363) if is_local_server_running(): - print("\n✓ Using existing local TerminusDB test server at http://127.0.0.1:6363") - print("⚠️ WARNING: Local server should be started with TERMINUSDB_AUTOLOGIN=true") - print(" Or use: TERMINUSDB_SERVER_AUTOLOGIN=true ./tests/terminusdb-test-server.sh restart") + print( + "\n✓ Using existing local TerminusDB test server at http://127.0.0.1:6363" + ) + print( + "⚠️ WARNING: Local server should be started with TERMINUSDB_AUTOLOGIN=true" + ) + print( + " Or use: TERMINUSDB_SERVER_AUTOLOGIN=true ./tests/terminusdb-test-server.sh restart" + ) yield "http://127.0.0.1:6363" return # Don't clean up - server was already running @@ -200,7 +211,9 @@ def docker_url(pytestconfig): response = requests.get(test_url) # Server responds with 404 for root path, which means it's running assert response.status_code in [200, 404] - print(f"✓ Docker container started successfully after {seconds_waited}s") + print( + f"✓ Docker container started successfully after {seconds_waited}s" + ) break except (requests.exceptions.ConnectionError, AssertionError): pass @@ -210,7 +223,9 @@ def docker_url(pytestconfig): if seconds_waited > MAX_CONTAINER_STARTUP_TIME: clean_up_container() - raise RuntimeError(f"Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)") + raise RuntimeError( + f"Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)" + ) yield test_url clean_up_container() diff --git a/terminusdb_client/woqlquery/woql_query.py b/terminusdb_client/woqlquery/woql_query.py index 588ed197..03829760 100644 --- a/terminusdb_client/woqlquery/woql_query.py +++ b/terminusdb_client/woqlquery/woql_query.py @@ -411,8 +411,8 @@ def _clean_object(self, user_obj, target=None): obj["node"] = user_obj elif type(user_obj) is list: elts = [] - for obj in user_obj: - elts.append(self._clean_object(obj)) + for item in user_obj: + elts.append(self._clean_object(item)) return elts elif isinstance(user_obj, Var): return self._expand_value_variable(user_obj) @@ -1510,10 +1510,14 @@ def woql_as(self, *args): def file(self, fpath, opts=None): """Provides details of a file source in a JSON format that includes a URL property + Note: CSV files can no longer be read from the filesystem. Only files submitted + as part of the request can be processed. Use remote() for URLs or submit files + via the API. + Parameters ---------- - fpath : dict - file data source in a JSON format + fpath : dict or str + file data source in a JSON format or file path opts : input options optional @@ -1523,7 +1527,7 @@ def file(self, fpath, opts=None): query object that can be chained and/or execute Example ------- - To load a local csv file: + To reference a file (must be submitted with request): >>> WOQLQuery().file("/app/local_files/my.csv") See Also -------- @@ -1537,8 +1541,10 @@ def file(self, fpath, opts=None): if self._cursor.get("@type"): self._wrap_cursor_with_and() self._cursor["@type"] = "QueryResource" - fpath["@type"] = "Source" - self._cursor["source"] = fpath + if isinstance(fpath, str): + self._cursor["source"] = {"@type": "Source", "file": fpath} + else: + self._cursor["source"] = fpath self._cursor["format"] = "csv" if opts: self._cursor["options"] = opts @@ -1600,21 +1606,45 @@ def remote(self, uri, opts=None): if self._cursor.get("@type"): self._wrap_cursor_with_and() self._cursor["@type"] = "QueryResource" - uri["@type"] = "Source" - self._cursor["source"] = uri + if isinstance(uri, dict): + uri["@type"] = "Source" + self._cursor["source"] = uri + else: + self._cursor["source"] = {"@type": "Source", "url": uri} self._cursor["format"] = "csv" if opts: self._cursor["options"] = opts return self def post(self, fpath, opts=None): + """Specifies a file to be posted as part of the request for processing. + + Note: CSV files can no longer be read from the filesystem. Only files submitted + as part of the request can be processed. This method should be used with files + that are uploaded via the API. + + Parameters + ---------- + fpath : str or dict + file path/identifier or dict with file details + opts : dict, optional + additional options for file processing + + Returns + ------- + WOQLQuery object + query object that can be chained and/or execute + """ if fpath and fpath == "args": return ["source", "format", "options"] if self._cursor.get("@type"): self._wrap_cursor_with_and() self._cursor["@type"] = "QueryResource" - fpath["@type"] = "Source" - self._cursor["source"] = fpath + if isinstance(fpath, dict): + fpath["@type"] = "Source" + self._cursor["source"] = fpath + else: + self._cursor["source"] = {"@type": "Source", "post": fpath} self._cursor["format"] = "csv" if opts: self._cursor["options"] = opts