Overview
Implement a compatibility wrapper (OFSCCompat) in ofsc/compat.py that allows existing synchronous OFSC client code to continue working in OFSC 3.0 while using AsyncOFSC internally.
In OFSC 3.0, the OFSC class will become an alias for OFSCCompat, so existing code continues to work without modification — but with a DeprecationWarning.
Background
When this issue was originally filed, the sync and async clients had similar method coverage. The async client has since grown to ~200 methods while the sync client covers ~97. The compatibility wrapper must cover the full async surface, not just the original sync subset.
Design Decisions
1. Method Scope
Wrap all ~200 async methods across all API modules:
core — Activities, Resources, Users, Events, Daily Extract, Subscriptions
metadata — Properties, Workzones, Workskills, Activity Types, Routing Profiles, etc.
capacity — Available capacity and quota
oauth2 — Token management
2. Async Execution Strategy
Use asyncio.run() per call. No thread pool or persistent event loop.
def get_workzones(self, offset=0, limit=100, **kwargs):
return asyncio.run(self._async_impl.get_workzones(offset=offset, limit=limit))
3. response_type Deprecation
The existing response_type parameter (FULL_RESPONSE, OBJ_RESPONSE, TEXT_RESPONSE) is a sync-only concept. In the compat wrapper:
- All
response_type values are deprecated and will emit DeprecationWarning when used.
- For
FULL_RESPONSE: return a RequestsResponseAdapter (see below) wrapping the httpx.Response to mimic requests.Response.
- For
OBJ_RESPONSE (default): return the Pydantic model as normal.
- For
TEXT_RESPONSE: return response.text.
The constants FULL_RESPONSE, OBJ_RESPONSE, TEXT_RESPONSE, and the wrap_return helper are deprecated alongside the sync client.
4. RequestsResponseAdapter
A thin wrapper around httpx.Response that mimics the requests.Response interface for users who rely on response_type=FULL_RESPONSE:
class RequestsResponseAdapter:
"""Wraps httpx.Response to mimic requests.Response interface."""
def __init__(self, httpx_response: httpx.Response): ...
@property
def status_code(self) -> int: ...
@property
def text(self) -> str: ...
def json(self) -> dict: ...
@property
def headers(self) -> dict: ...
@property
def content(self) -> bytes: ...
@property
def ok(self) -> bool: ...
5. File Location
New file: ofsc/compat.py
In OFSC 3.0, ofsc/__init__.py will re-export:
from ofsc.compat import OFSCCompat as OFSC
6. Deprecation Warning
Emit DeprecationWarning on instantiation of OFSC / OFSCCompat:
warnings.warn(
"OFSC sync client is deprecated and will be removed in OFSC 4.0. "
"Use AsyncOFSC instead.",
DeprecationWarning,
stacklevel=2,
)
Architecture Sketch
# ofsc/compat.py
class RequestsResponseAdapter:
"""Wraps httpx.Response to mimic requests.Response interface."""
...
class _SyncModuleWrapper:
"""Generic wrapper that converts async module methods to sync."""
def __init__(self, async_module):
self._async = async_module
def __getattr__(self, name):
attr = getattr(self._async, name)
if asyncio.iscoroutinefunction(attr):
def sync_wrapper(*args, **kwargs):
response_type = kwargs.pop("response_type", None)
if response_type is not None:
warnings.warn("response_type is deprecated...", DeprecationWarning, stacklevel=2)
result = asyncio.run(attr(*args, **kwargs))
# Handle response_type adaptation here
return result
return sync_wrapper
return attr
class OFSCCompat:
"""Synchronous compatibility wrapper for AsyncOFSC."""
def __init__(self, clientID, secret, companyName, **kwargs):
warnings.warn("OFSC sync client is deprecated...", DeprecationWarning, stacklevel=2)
self._async_client = AsyncOFSC(clientID=clientID, secret=secret, companyName=companyName, **kwargs)
...
@property
def core(self): ...
@property
def metadata(self): ...
@property
def capacity(self): ...
@property
def oauth2(self): ...
Testing Requirements
Migration Path
# Before (OFSC 2.x sync)
instance = OFSC(clientID=..., secret=..., companyName=...)
workzones = instance.metadata.get_workzones()
# After (OFSC 3.0+ async — recommended)
async with AsyncOFSC(clientID=..., secret=..., companyName=...) as client:
workzones = await client.metadata.get_workzones()
# Compat (OFSC 3.0 — works but deprecated)
instance = OFSC(clientID=..., secret=..., companyName=...) # DeprecationWarning
workzones = instance.metadata.get_workzones() # Works via asyncio.run()
Related
ofsc/async_client/ — source of all wrapped methods
- README deprecation notice: "Future Deprecation Notice - OFSC 3.0"
FULL_RESPONSE, OBJ_RESPONSE, TEXT_RESPONSE, wrap_return in ofsc/__init__.py
Overview
Implement a compatibility wrapper (
OFSCCompat) inofsc/compat.pythat allows existing synchronousOFSCclient code to continue working in OFSC 3.0 while usingAsyncOFSCinternally.In OFSC 3.0, the
OFSCclass will become an alias forOFSCCompat, so existing code continues to work without modification — but with aDeprecationWarning.Background
When this issue was originally filed, the sync and async clients had similar method coverage. The async client has since grown to ~200 methods while the sync client covers ~97. The compatibility wrapper must cover the full async surface, not just the original sync subset.
Design Decisions
1. Method Scope
Wrap all ~200 async methods across all API modules:
core— Activities, Resources, Users, Events, Daily Extract, Subscriptionsmetadata— Properties, Workzones, Workskills, Activity Types, Routing Profiles, etc.capacity— Available capacity and quotaoauth2— Token management2. Async Execution Strategy
Use
asyncio.run()per call. No thread pool or persistent event loop.3.
response_typeDeprecationThe existing
response_typeparameter (FULL_RESPONSE,OBJ_RESPONSE,TEXT_RESPONSE) is a sync-only concept. In the compat wrapper:response_typevalues are deprecated and will emitDeprecationWarningwhen used.FULL_RESPONSE: return aRequestsResponseAdapter(see below) wrapping thehttpx.Responseto mimicrequests.Response.OBJ_RESPONSE(default): return the Pydantic model as normal.TEXT_RESPONSE: returnresponse.text.The constants
FULL_RESPONSE,OBJ_RESPONSE,TEXT_RESPONSE, and thewrap_returnhelper are deprecated alongside the sync client.4.
RequestsResponseAdapterA thin wrapper around
httpx.Responsethat mimics therequests.Responseinterface for users who rely onresponse_type=FULL_RESPONSE:5. File Location
New file:
ofsc/compat.pyIn OFSC 3.0,
ofsc/__init__.pywill re-export:6. Deprecation Warning
Emit
DeprecationWarningon instantiation ofOFSC/OFSCCompat:Architecture Sketch
Testing Requirements
OFSCCompatinstantiation emitsDeprecationWarningcore,metadata,capacity,oauth2) are accessibleresponse_type=FULL_RESPONSEreturns aRequestsResponseAdapterresponse_type=OBJ_RESPONSEreturns the Pydantic model (with deprecation warning)response_type=TEXT_RESPONSEreturns response text (with deprecation warning)RequestsResponseAdapterexposesstatus_code,text,json(),headers,content,okresponse_typeemitsDeprecationWarningOFSCCompatMigration Path
Related
ofsc/async_client/— source of all wrapped methodsFULL_RESPONSE,OBJ_RESPONSE,TEXT_RESPONSE,wrap_returninofsc/__init__.py