Packages
snakepit
0.7.1
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
High-performance pooler and session manager for external language integrations. Supports Python, Node.js, Ruby, and more with gRPC streaming, session management, and production-ready process cleanup.
Current section
Files
Jump to
Current section
Files
lib/snakepit/compatibility.ex
defmodule Snakepit.Compatibility do
@moduledoc """
Library compatibility checking for thread-safe execution.
This module maintains a compatibility matrix of popular Python libraries
and their thread safety characteristics. It helps users understand which
libraries work well with the `:thread` worker profile vs `:process` profile.
## Usage
# Check if a library is thread-safe
case Snakepit.Compatibility.check("numpy", :thread) do
:ok -> "Safe to use"
{:warning, msg} -> "Warning: " <> msg
{:unknown, msg} -> "Unknown: " <> msg
end
# Get full library info
{:ok, info} = Snakepit.Compatibility.get_library_info("torch")
## Compatibility Status
- **Thread-safe**: Library can be used safely with `:thread` profile
- **Not thread-safe**: Library should only be used with `:process` profile
- **Conditional**: Thread-safe with specific configuration
- **Unknown**: Thread safety not verified
## Sources
Compatibility information is gathered from:
- Official library documentation
- https://py-free-threading.github.io/tracking/
- https://hugovk.github.io/free-threaded-wheels/
- Community testing and reports
"""
@type thread_safe :: boolean()
@type profile :: :process | :thread
@type check_result :: :ok | {:warning, String.t()} | {:unknown, String.t()}
# Compatibility matrix
# Updated based on official Python 3.13+ free-threading documentation
@library_compatibility %{
# Scientific Computing - Generally thread-safe with GIL released
"numpy" => %{
thread_safe: true,
notes: "Releases GIL during computation. Thread-safe since 1.20.0",
min_version: "1.20.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released"
},
"scipy" => %{
thread_safe: true,
notes: "Releases GIL for numerical operations",
min_version: "1.7.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released"
},
# Deep Learning - Most are thread-safe
"torch" => %{
thread_safe: true,
notes: "Thread-safe with proper initialization. Use torch.set_num_threads()",
min_version: "2.0.0",
recommendation: "Safe for :thread profile with configuration",
gil_behavior: "released",
workaround: "Set torch.set_num_threads() per worker"
},
"pytorch" => %{
thread_safe: true,
notes: "Alias for torch",
min_version: "2.0.0",
recommendation: "Safe for :thread profile with configuration",
gil_behavior: "released"
},
"tensorflow" => %{
thread_safe: true,
notes: "Thread-safe. Configure thread pools appropriately",
min_version: "2.10.0",
recommendation: "Safe for :thread profile with configuration",
gil_behavior: "released",
workaround: "Use tf.config.threading API"
},
# Data Processing - Varies
"pandas" => %{
thread_safe: false,
notes: "Not thread-safe as of v2.0. DataFrame operations may race",
min_version: nil,
recommendation: "Use :process profile",
gil_behavior: "held",
workaround: "Use with explicit locking or :process profile"
},
"polars" => %{
thread_safe: true,
notes: "Designed for parallel execution",
min_version: "0.16.0",
recommendation: "Excellent for :thread profile",
gil_behavior: "released"
},
# Visualization - Generally not thread-safe
"matplotlib" => %{
thread_safe: false,
notes: "Not thread-safe. Use thread-local figures or explicit locking",
min_version: nil,
recommendation: "Use :process profile or thread-local figures",
gil_behavior: "held",
workaround: "Use thread-local storage: threading.local()"
},
"plotly" => %{
thread_safe: true,
notes: "Thread-safe for figure creation",
min_version: "5.0.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released"
},
# Database - Mixed
"sqlite3" => %{
thread_safe: false,
notes: "Not thread-safe by default. Use check_same_thread=False with caution",
min_version: nil,
recommendation: "Use :process profile or connection pooling",
gil_behavior: "held",
workaround: "Use connection per thread or :process profile"
},
"psycopg2" => %{
thread_safe: true,
notes: "Thread-safe at connection level (not cursor level)",
min_version: "2.9.0",
recommendation: "Safe for :thread profile with connection per thread",
gil_behavior: "released"
},
# Web - Generally safe
"requests" => %{
thread_safe: true,
notes: "Thread-safe for separate sessions",
min_version: "2.25.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released"
},
"httpx" => %{
thread_safe: true,
notes: "Async-first, thread-safe",
min_version: "0.23.0",
recommendation: "Excellent for :thread profile",
gil_behavior: "released"
},
# ML Libraries
"scikit-learn" => %{
thread_safe: true,
notes: "Thread-safe with n_jobs parameter control",
min_version: "1.0.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released",
workaround: "Set n_jobs=1 per estimator to avoid over-subscription"
},
"xgboost" => %{
thread_safe: true,
notes: "Thread-safe with nthread parameter",
min_version: "1.6.0",
recommendation: "Safe for :thread profile with configuration",
gil_behavior: "released",
workaround: "Set nthread parameter appropriately"
},
"lightgbm" => %{
thread_safe: true,
notes: "Thread-safe with num_threads parameter",
min_version: "3.3.0",
recommendation: "Safe for :thread profile with configuration",
gil_behavior: "released",
workaround: "Set num_threads parameter"
},
# NLP
"transformers" => %{
thread_safe: true,
notes: "Thread-safe for inference with separate model instances",
min_version: "4.20.0",
recommendation: "Safe for :thread profile",
gil_behavior: "released"
},
"spacy" => %{
thread_safe: true,
notes: "Thread-safe with separate nlp instances per thread",
min_version: "3.4.0",
recommendation: "Safe for :thread profile with thread-local models",
gil_behavior: "released",
workaround: "Use threading.local() for nlp instances"
}
}
@doc """
Check if a library is compatible with a given worker profile.
Returns:
- `:ok` - Library is safe to use
- `{:warning, message}` - Library may have issues
- `{:unknown, message}` - Compatibility unknown
## Examples
iex> Snakepit.Compatibility.check("numpy", :thread)
:ok
iex> Snakepit.Compatibility.check("pandas", :thread)
{:warning, "Not thread-safe as of v2.0..."}
iex> Snakepit.Compatibility.check("unknown_lib", :thread)
{:unknown, "Compatibility unknown for unknown_lib"}
"""
@spec check(library :: String.t(), profile) :: check_result()
def check(library, profile) do
case Map.get(@library_compatibility, library) do
nil ->
{:unknown, "Compatibility unknown for #{library}. Check library documentation."}
info ->
check_compatibility(library, info, profile)
end
end
@doc """
Get detailed information about a library's compatibility.
## Examples
iex> Snakepit.Compatibility.get_library_info("torch")
{:ok, %{
thread_safe: true,
notes: "Thread-safe with proper initialization...",
recommendation: "Safe for :thread profile with configuration"
}}
"""
@spec get_library_info(String.t()) :: {:ok, map()} | {:error, :not_found}
def get_library_info(library) do
case Map.get(@library_compatibility, library) do
nil -> {:error, :not_found}
info -> {:ok, info}
end
end
@doc """
List all libraries in the compatibility matrix.
Returns a map grouped by thread safety status.
## Examples
iex> Snakepit.Compatibility.list_all()
%{
thread_safe: ["numpy", "torch", ...],
not_thread_safe: ["pandas", "matplotlib", ...],
conditional: ["scikit-learn", ...]
}
"""
@spec list_all() :: map()
def list_all do
Enum.group_by(@library_compatibility, fn {_lib, info} ->
cond do
info.thread_safe and Map.has_key?(info, :workaround) -> :conditional
info.thread_safe -> :thread_safe
true -> :not_thread_safe
end
end)
|> Map.new(fn {category, libs} ->
{category, Enum.map(libs, fn {lib, _info} -> lib end) |> Enum.sort()}
end)
end
@doc """
Generate a compatibility report for a list of libraries.
Useful for validating an adapter's dependencies before deployment.
## Examples
iex> Snakepit.Compatibility.generate_report(["numpy", "pandas"], :thread)
%{
compatible: ["numpy"],
warnings: [{"pandas", "Not thread-safe..."}],
unknown: []
}
"""
@spec generate_report([String.t()], profile) :: map()
def generate_report(libraries, profile) do
results =
Enum.map(libraries, fn lib ->
{lib, check(lib, profile)}
end)
%{
compatible: for({lib, :ok} <- results, do: lib),
warnings: for({lib, {:warning, msg}} <- results, do: {lib, msg}),
unknown: for({lib, {:unknown, msg}} <- results, do: {lib, msg})
}
end
@doc """
Check if a specific Python version is recommended for a library.
Some libraries require specific Python versions for free-threading support.
## Examples
iex> Snakepit.Compatibility.check_python_version("numpy", {3, 13, 0})
:ok
iex> Snakepit.Compatibility.check_python_version("pandas", {3, 13, 0})
{:warning, "Library may not be optimized for Python 3.13 free-threading"}
"""
@spec check_python_version(String.t(), {integer(), integer(), integer()}) :: check_result()
def check_python_version(library, python_version) do
case get_library_info(library) do
{:ok, info} ->
{py_major, py_minor, _py_patch} = python_version
cond do
info.thread_safe and py_major == 3 and py_minor >= 13 ->
:ok
not info.thread_safe and py_major == 3 and py_minor >= 13 ->
{:warning,
"Library #{library} is not thread-safe. " <>
"Use :process profile even with Python 3.13+"}
py_major == 3 and py_minor < 13 ->
{:warning,
"Python 3.13+ recommended for free-threading. " <>
"Use :process profile with Python #{py_major}.#{py_minor}"}
true ->
:ok
end
{:error, :not_found} ->
{:unknown, "Unknown library #{library}"}
end
end
# Private helpers
defp check_compatibility(library, info, profile) do
case profile do
:process ->
# Process profile is always safe (isolation)
:ok
:thread ->
if info.thread_safe do
# Thread-safe, but may have configuration requirements
if Map.has_key?(info, :workaround) do
{:warning, "#{library} is thread-safe but requires configuration: #{info.workaround}"}
else
:ok
end
else
# Not thread-safe
{:warning,
"#{library} is not thread-safe. #{info.notes}. Recommendation: #{info.recommendation}"}
end
end
end
end