isolate 0.14.2__tar.gz → 0.15.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.

Potentially problematic release.


This version of isolate might be problematic. Click here for more details.

Files changed (86) hide show
  1. {isolate-0.14.2 → isolate-0.15.0}/.github/workflows/test.yml +1 -1
  2. {isolate-0.14.2 → isolate-0.15.0}/.pre-commit-config.yaml +4 -1
  3. {isolate-0.14.2 → isolate-0.15.0}/PKG-INFO +1 -1
  4. {isolate-0.14.2 → isolate-0.15.0}/pyproject.toml +4 -0
  5. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/_isolate_version.py +2 -2
  6. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/settings.py +32 -2
  7. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/_local/_base.py +3 -1
  8. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/_base.py +3 -1
  9. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/ipc/_base.py +3 -1
  10. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/logger.py +4 -0
  11. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/PKG-INFO +1 -1
  12. {isolate-0.14.2 → isolate-0.15.0}/tests/test_backends.py +41 -0
  13. {isolate-0.14.2 → isolate-0.15.0}/tests/test_server.py +5 -3
  14. {isolate-0.14.2 → isolate-0.15.0}/.github/workflows/release.yml +0 -0
  15. {isolate-0.14.2 → isolate-0.15.0}/.gitignore +0 -0
  16. {isolate-0.14.2 → isolate-0.15.0}/LICENSE +0 -0
  17. {isolate-0.14.2 → isolate-0.15.0}/README.md +0 -0
  18. {isolate-0.14.2 → isolate-0.15.0}/setup.cfg +0 -0
  19. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/__init__.py +0 -0
  20. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/_version.py +0 -0
  21. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/__init__.py +0 -0
  22. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/_base.py +0 -0
  23. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/common.py +0 -0
  24. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/conda.py +0 -0
  25. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/container.py +0 -0
  26. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/local.py +0 -0
  27. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/pyenv.py +0 -0
  28. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/remote.py +0 -0
  29. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/backends/virtualenv.py +0 -0
  30. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/common/__init__.py +0 -0
  31. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/common/timestamp.py +0 -0
  32. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/__init__.py +0 -0
  33. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/_local/__init__.py +0 -0
  34. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/_local/agent_startup.py +0 -0
  35. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/common.py +0 -0
  36. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/__init__.py +0 -0
  37. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/agent.py +0 -0
  38. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/configuration.py +0 -0
  39. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/__init__.py +0 -0
  40. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/agent.proto +0 -0
  41. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/agent_pb2.py +0 -0
  42. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/agent_pb2.pyi +0 -0
  43. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/agent_pb2_grpc.py +0 -0
  44. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/common.proto +0 -0
  45. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/common_pb2.py +0 -0
  46. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/common_pb2.pyi +0 -0
  47. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/definitions/common_pb2_grpc.py +0 -0
  48. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/grpc/interface.py +0 -0
  49. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/ipc/__init__.py +0 -0
  50. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/connections/ipc/agent.py +0 -0
  51. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/logs.py +0 -0
  52. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/py.typed +0 -0
  53. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/registry.py +0 -0
  54. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/__init__.py +0 -0
  55. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/definitions/__init__.py +0 -0
  56. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/definitions/server.proto +0 -0
  57. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/definitions/server_pb2.py +0 -0
  58. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/definitions/server_pb2.pyi +0 -0
  59. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/definitions/server_pb2_grpc.py +0 -0
  60. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health/__init__.py +0 -0
  61. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health/health.proto +0 -0
  62. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health/health_pb2.py +0 -0
  63. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health/health_pb2.pyi +0 -0
  64. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health/health_pb2_grpc.py +0 -0
  65. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/health_server.py +0 -0
  66. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/interface.py +0 -0
  67. {isolate-0.14.2 → isolate-0.15.0}/src/isolate/server/server.py +0 -0
  68. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/SOURCES.txt +0 -0
  69. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/dependency_links.txt +0 -0
  70. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/entry_points.txt +0 -0
  71. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/requires.txt +0 -0
  72. {isolate-0.14.2 → isolate-0.15.0}/src/isolate.egg-info/top_level.txt +0 -0
  73. {isolate-0.14.2 → isolate-0.15.0}/tests/__init__.py +0 -0
  74. {isolate-0.14.2 → isolate-0.15.0}/tests/conftest.py +0 -0
  75. {isolate-0.14.2 → isolate-0.15.0}/tests/test_concurrency.py +0 -0
  76. {isolate-0.14.2 → isolate-0.15.0}/tests/test_connections.py +0 -0
  77. {isolate-0.14.2 → isolate-0.15.0}/tests/test_isolate.py +0 -0
  78. {isolate-0.14.2 → isolate-0.15.0}/tests/test_log.py +0 -0
  79. {isolate-0.14.2 → isolate-0.15.0}/tests/test_logger.py +0 -0
  80. {isolate-0.14.2 → isolate-0.15.0}/tests/test_serialization.py +0 -0
  81. {isolate-0.14.2 → isolate-0.15.0}/tools/Dockerfile +0 -0
  82. {isolate-0.14.2 → isolate-0.15.0}/tools/agent_requirements.txt +0 -0
  83. {isolate-0.14.2 → isolate-0.15.0}/tools/protobuf-requirements.txt +0 -0
  84. {isolate-0.14.2 → isolate-0.15.0}/tools/regen_grpc.py +0 -0
  85. {isolate-0.14.2 → isolate-0.15.0}/tools/requirements.txt +0 -0
  86. {isolate-0.14.2 → isolate-0.15.0}/tools/test_agent_requirements.txt +0 -0
@@ -17,7 +17,7 @@ jobs:
17
17
  test:
18
18
  runs-on: ${{ matrix.os }}
19
19
  strategy:
20
- fail-fast: true
20
+ fail-fast: false
21
21
 
22
22
  matrix:
23
23
  os: [ubuntu-latest]
@@ -3,7 +3,10 @@ repos:
3
3
  rev: 'v0.3.4'
4
4
  hooks:
5
5
  - id: ruff
6
- args: [--fix, --exit-non-zero-on-fix]
6
+ args:
7
+ - --fix
8
+ - --exit-non-zero-on-fix
9
+ - --exclude=UP007
7
10
  exclude: ".*(_pb2.py|_pb2.pyi|_pb2_grpc.py)$"
8
11
  - id: ruff-format
9
12
  exclude: ".*(_pb2.py|_pb2.pyi|_pb2_grpc.py)$"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isolate
3
- Version: 0.14.2
3
+ Version: 0.15.0
4
4
  Summary: Managed isolated environments for Python
5
5
  Author-email: Features & Labels <hello@fal.ai>
6
6
  Project-URL: Issues, https://github.com/fal-ai/isolate/issues
@@ -67,5 +67,9 @@ dev = [
67
67
  target-version = "py38"
68
68
  exclude = ["*_pb2.py", "*_pb2.pyi", "*_pb2_grpc.py"]
69
69
 
70
+ [tool.ruff.lint.pyupgrade]
71
+ # Preserve types, even if a file imports `from __future__ import annotations`.
72
+ keep-runtime-typing = true
73
+
70
74
  [tool.ruff.lint]
71
75
  select = ["E", "F", "W", "PLC", "PLE", "PLW", "I", "UP"]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.14.2'
16
- __version_tuple__ = version_tuple = (0, 14, 2)
15
+ __version__ = version = '0.15.0'
16
+ __version_tuple__ = version_tuple = (0, 15, 0)
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Callable, Iterator
11
11
  from platformdirs import user_cache_dir
12
12
 
13
13
  from isolate.backends.common import lock_build_path
14
- from isolate.logs import Log
14
+ from isolate.logs import Log, LogLevel, LogSource
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from isolate.backends import BaseEnvironment
@@ -28,7 +28,37 @@ class IsolateSettings:
28
28
  strict_cache: bool = _STRICT_CACHE
29
29
 
30
30
  def log(self, log: Log) -> None:
31
- self.log_hook(log)
31
+ self.log_hook(self._infer_log_level(log))
32
+
33
+ def _infer_log_level(self, log: Log) -> Log:
34
+ """Infer the log level if it's correctly set."""
35
+ if log.level not in (LogLevel.STDOUT, LogLevel.STDERR):
36
+ # We should only infer the log level for stdout/stderr logs.
37
+ return log
38
+
39
+ if log.source in (LogSource.BUILDER, LogSource.BRIDGE):
40
+ return replace(log, level=LogLevel.TRACE)
41
+
42
+ line = log.message.lower()
43
+
44
+ if "[error]" in line:
45
+ return replace(log, level=LogLevel.ERROR)
46
+ if "[warning]" in line:
47
+ return replace(log, level=LogLevel.WARNING)
48
+ if "[warn]" in line:
49
+ return replace(log, level=LogLevel.WARNING)
50
+ if "[info]" in line:
51
+ return replace(log, level=LogLevel.INFO)
52
+ if "[debug]" in line:
53
+ return replace(log, level=LogLevel.DEBUG)
54
+ if "[trace]" in line:
55
+ return replace(log, level=LogLevel.TRACE)
56
+
57
+ if log.level == LogLevel.STDERR:
58
+ return replace(log, level=LogLevel.ERROR)
59
+
60
+ # Default to INFO level
61
+ return replace(log, level=LogLevel.INFO)
32
62
 
33
63
  def _get_temp_base(self) -> Path:
34
64
  """Return the base path for creating temporary files/directories.
@@ -173,7 +173,9 @@ class PythonExecutionBase(Generic[ConnectionType]):
173
173
  """Return the command to run the agent process with."""
174
174
  raise NotImplementedError
175
175
 
176
- def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
176
+ def handle_agent_log(
177
+ self, line: str, *, level: LogLevel, source: LogSource
178
+ ) -> None:
177
179
  """Handle a log line emitted by the agent process. The level will be either
178
180
  STDOUT or STDERR."""
179
181
  raise NotImplementedError
@@ -147,5 +147,7 @@ class LocalPythonGRPC(PythonExecutionBase[str], GRPCExecutionBase):
147
147
  str(log_fd),
148
148
  ]
149
149
 
150
- def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
150
+ def handle_agent_log(
151
+ self, line: str, *, level: LogLevel, source: LogSource
152
+ ) -> None:
151
153
  self.log(line, level=level, source=source)
@@ -219,5 +219,7 @@ class PythonIPC(PythonExecutionBase[AgentListener], IsolatedProcessConnection):
219
219
  str(log_fd),
220
220
  ]
221
221
 
222
- def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
222
+ def handle_agent_log(
223
+ self, line: str, *, level: LogLevel, source: LogSource
224
+ ) -> None:
223
225
  self.log(line, level=level, source=source)
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ from datetime import datetime, timezone
3
4
  from typing import Dict
4
5
 
5
6
  from isolate.logs import LogLevel, LogSource
@@ -16,6 +17,9 @@ class IsolateLogger:
16
17
 
17
18
  def log(self, level: LogLevel, message: str, source: LogSource) -> None:
18
19
  record = {
20
+ # Set the timestamp from source so we can be sure no buffering or
21
+ # latency is affecting the timestamp.
22
+ "logged_at": datetime.now(tz=timezone.utc).isoformat(),
19
23
  "isolate_source": source.name,
20
24
  "level": level.name,
21
25
  "message": message,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isolate
3
- Version: 0.14.2
3
+ Version: 0.15.0
4
4
  Summary: Managed isolated environments for Python
5
5
  Author-email: Features & Labels <hello@fal.ai>
6
6
  Project-URL: Issues, https://github.com/fal-ai/isolate/issues
@@ -16,6 +16,7 @@ from isolate.backends.pyenv import PyenvEnvironment, _get_pyenv_executable
16
16
  from isolate.backends.remote import IsolateServer
17
17
  from isolate.backends.settings import IsolateSettings
18
18
  from isolate.backends.virtualenv import VirtualPythonEnvironment
19
+ from isolate.logs import LogLevel, LogSource
19
20
 
20
21
 
21
22
  class GenericEnvironmentTests:
@@ -660,6 +661,46 @@ def test_isolate_server_logs(isolate_server):
660
661
  assert "hello!!!" in [log.message for log in collected_logs]
661
662
 
662
663
 
664
+ def test_isolate_server_logs_level_inference(isolate_server):
665
+ collected_logs = []
666
+ environment = IsolateServer(
667
+ host=isolate_server,
668
+ target_environments=[
669
+ {
670
+ "kind": "virtualenv",
671
+ "configuration": {"requirements": ["pyjokes==0.5.0"]},
672
+ }
673
+ ],
674
+ )
675
+
676
+ environment.apply_settings(IsolateSettings(log_hook=collected_logs.append))
677
+
678
+ with environment.connect() as connection:
679
+ connection.run(
680
+ partial(
681
+ eval,
682
+ "print('[debug] hello!!!') or "
683
+ "print('[error] bad!!!') or "
684
+ "print('[trace] hide!!!') or "
685
+ "print('[warning] warn1!!!') or "
686
+ "print('warn2!!! [warn]') or "
687
+ "print('default') or "
688
+ "__import__('time').sleep(0.5)",
689
+ )
690
+ )
691
+
692
+ user_logs = [log for log in collected_logs if log.source == LogSource.USER]
693
+ assert len(user_logs) == 6
694
+ assert [log.level for log in user_logs] == [
695
+ LogLevel.DEBUG,
696
+ LogLevel.ERROR,
697
+ LogLevel.TRACE,
698
+ LogLevel.WARNING,
699
+ LogLevel.WARNING,
700
+ LogLevel.INFO,
701
+ ]
702
+
703
+
663
704
  def test_isolate_server_demo(isolate_server):
664
705
  from functools import partial
665
706
 
@@ -252,6 +252,7 @@ def test_user_logs_immediate(stub: definitions.IsolateStub, monkeypatch: Any) ->
252
252
  import sys, pyjokes
253
253
  print(pyjokes.__version__)
254
254
  print("error error!", file=sys.stderr)
255
+ print("[debug] error!", file=sys.stderr)
255
256
  """
256
257
  ),
257
258
  ),
@@ -263,11 +264,12 @@ def test_user_logs_immediate(stub: definitions.IsolateStub, monkeypatch: Any) ->
263
264
  user_logs: List[Log] = []
264
265
  run_request(stub, request, user_logs=user_logs)
265
266
 
266
- assert len(user_logs) == 2
267
+ assert len(user_logs) == 3
267
268
 
268
269
  by_stream = {log.level: log.message for log in user_logs}
269
- assert by_stream[LogLevel.STDOUT] == "0.6.0"
270
- assert by_stream[LogLevel.STDERR] == "error error!"
270
+ assert by_stream[LogLevel.INFO] == "0.6.0"
271
+ assert by_stream[LogLevel.ERROR] == "error error!"
272
+ assert by_stream[LogLevel.DEBUG] == "[debug] error!"
271
273
 
272
274
 
273
275
  def test_unknown_environment(stub: definitions.IsolateStub, monkeypatch: Any) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes