Skip to content

Commit

Permalink
allows disabling enum enforcer on all client methods. (#303)
Browse files Browse the repository at this point in the history
* allowed disabling enum enforcer on all client methods. tests and docs modified as per changes

Adds relevant test cases

* reduced all lines to under 79

* formatting changes again

* removed additional assertions from test cases
  • Loading branch information
pssolanki111 authored Apr 17, 2022
1 parent b815f0e commit 53db610
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ venv/
ENV/
env.bak/
venv.bak/
.idea

# Spyder project settings
.spyderproject
Expand Down
9 changes: 8 additions & 1 deletion docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,14 @@ Creating a New Client
+++++++++++++++++++++

99.9% of users should not create their own clients, and should instead follow
the instructions outlined in :ref:`auth`. For those brave enough to build their
the instructions outlined in :ref:`auth`.

For users who want to disable the strict enum type checking on http client,
just pass ``enforce_enums=False`` in any of the client creation functions
described in :ref:`auth`. Just note that for most users, it is advised they
stick with the default behavior.

For those brave enough to build their
own, the constructor looks like this:

.. automethod:: tda.client.Client.__init__
Expand Down
69 changes: 56 additions & 13 deletions tda/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def _register_token_redactions(token):
register_redactions(token)


def client_from_token_file(token_path, api_key, asyncio=False):
def client_from_token_file(token_path, api_key, asyncio=False, enforce_enums=True):
'''
Returns a session from an existing token file. The session will perform
an auth refresh as needed. It will also update the token on disk whenever
Expand All @@ -82,16 +82,25 @@ def client_from_token_file(token_path, api_key, asyncio=False):
:func:`~tda.auth.easy_client` to create one.
:param api_key: Your TD Ameritrade application's API key, also known as the
client ID.
:param asyncio: If set to ``True``, this will enable async support allowing
the client to be used in an async environment. Defaults to
``False``
:param enforce_enums: Set it to ``False`` to disable the enum checks on ALL
the client methods. Only do it if you know you really
need it. For most users, it is advised to use enums
to avoid errors.
'''

load = __token_loader(token_path)

return client_from_access_functions(
api_key, load, __update_token(token_path), asyncio=asyncio)
api_key, load, __update_token(token_path), asyncio=asyncio,
enforce_enums=enforce_enums)


def __fetch_and_register_token_from_redirect(
oauth, redirected_url, api_key, token_path, token_write_func, asyncio):
oauth, redirected_url, api_key, token_path, token_write_func, asyncio,
enforce_enums=True):
token = oauth.fetch_token(
TOKEN_ENDPOINT,
authorization_response=redirected_url,
Expand Down Expand Up @@ -132,7 +141,7 @@ async def oauth_client_update_token(t, *args, **kwargs):
auto_refresh_url=TOKEN_ENDPOINT,
auto_refresh_kwargs={'client_id': api_key},
update_token=oauth_client_update_token),
token_metadata=metadata_manager)
token_metadata=metadata_manager, enforce_enums=enforce_enums)


class RedirectTimeoutError(Exception):
Expand Down Expand Up @@ -263,7 +272,8 @@ def ensure_refresh_token_update(
# TODO: Raise an exception when passing both token_path and token_write_func
def client_from_login_flow(webdriver, api_key, redirect_url, token_path,
redirect_wait_time_seconds=0.1, max_waits=3000,
asyncio=False, token_write_func=None):
asyncio=False, token_write_func=None,
enforce_enums=True):
'''
Uses the webdriver to perform an OAuth webapp login flow and creates a
client wrapped around the resulting token. The client will be configured to
Expand All @@ -281,6 +291,14 @@ def client_from_login_flow(webdriver, api_key, redirect_url, token_path,
:param token_path: Path to which the new token will be written. If the token
file already exists, it will be overwritten with a new
one. Updated tokens will be written to this path as well.
:param asyncio: If set to ``True``, this will enable async support allowing
the client to be used in an async environment. Defaults to
``False``
:param enforce_enums: Set it to ``False`` to disable the enum checks on ALL
the client methods. Only do it if you know you really
need it. For most users, it is advised to use enums
to avoid errors.
'''
get_logger().info('Creating new token with redirect URL \'%s\' ' +
'and token path \'%s\'', redirect_url, token_path)
Expand Down Expand Up @@ -328,11 +346,12 @@ def client_from_login_flow(webdriver, api_key, redirect_url, token_path,

return __fetch_and_register_token_from_redirect(
oauth, current_url, api_key, token_path, token_write_func,
asyncio)
asyncio, enforce_enums=enforce_enums)


def client_from_manual_flow(api_key, redirect_url, token_path,
asyncio=False, token_write_func=None):
asyncio=False, token_write_func=None,
enforce_enums=True):
'''
Walks the user through performing an OAuth login flow by manually
copy-pasting URLs, and returns a client wrapped around the resulting token.
Expand All @@ -351,6 +370,13 @@ def client_from_manual_flow(api_key, redirect_url, token_path,
:param token_path: Path to which the new token will be written. If the token
file already exists, it will be overwritten with a new
one. Updated tokens will be written to this path as well.
:param asyncio: If set to ``True``, this will enable async support allowing
the client to be used in an async environment. Defaults to
``False``
:param enforce_enums: Set it to ``False`` to disable the enum checks on ALL
the client methods. Only do it if you know you really
need it. For most users, it is advised to use enums
to avoid errors.
'''
get_logger().info('Creating new token with redirect URL \'%s\' ' +
'and token path \'%s\'', redirect_url, token_path)
Expand Down Expand Up @@ -397,11 +423,11 @@ def client_from_manual_flow(api_key, redirect_url, token_path,

return __fetch_and_register_token_from_redirect(
oauth, redirected_url, api_key, token_path, token_write_func,
asyncio)
asyncio, enforce_enums=enforce_enums)


def easy_client(api_key, redirect_uri, token_path, webdriver_func=None,
asyncio=False):
asyncio=False, enforce_enums=True):
'''Convenient wrapper around :func:`client_from_login_flow` and
:func:`client_from_token_file`. If ``token_path`` exists, loads the token
from it. Otherwise open a login flow to fetch a new token. Returns a client
Expand All @@ -426,11 +452,19 @@ def easy_client(api_key, redirect_uri, token_path, webdriver_func=None,
:param webdriver_func: Function that returns a webdriver for use in fetching
a new token. Will only be called if the token file
cannot be found.
:param asyncio: If set to ``True``, this will enable async support allowing
the client to be used in an async environment. Defaults to
``False``
:param enforce_enums: Set it to ``False`` to disable the enum checks on ALL
the client methods. Only do it if you know you really
need it. For most users, it is advised to use enums
to avoid errors.
'''
logger = get_logger()

if os.path.isfile(token_path):
c = client_from_token_file(token_path, api_key, asyncio=asyncio)
c = client_from_token_file(token_path, api_key, asyncio=asyncio,
enforce_enums=enforce_enums)
logger.info(
'Returning client loaded from token file \'%s\'', token_path)
return c
Expand All @@ -440,7 +474,8 @@ def easy_client(api_key, redirect_uri, token_path, webdriver_func=None,
if webdriver_func is not None:
with webdriver_func() as driver:
c = client_from_login_flow(
driver, api_key, redirect_uri, token_path, asyncio=asyncio)
driver, api_key, redirect_uri, token_path, asyncio=asyncio,
enforce_enums=enforce_enums)
logger.info(
'Returning client fetched using webdriver, writing' +
'token to \'%s\'', token_path)
Expand All @@ -451,7 +486,8 @@ def easy_client(api_key, redirect_uri, token_path, webdriver_func=None,


def client_from_access_functions(api_key, token_read_func,
token_write_func, asyncio=False):
token_write_func, asyncio=False,
enforce_enums=True):
'''
Returns a session from an existing token file, using the accessor methods to
read and write the token. This is an advanced method for users who do not
Expand All @@ -478,6 +514,13 @@ def client_from_access_functions(api_key, token_read_func,
called whenever the token is updated, such as when
it is refreshed. See the above-mentioned example
for what parameters this method takes.
:param asyncio: If set to ``True``, this will enable async support allowing
the client to be used in an async environment. Defaults to
``False``
:param enforce_enums: Set it to ``False`` to disable the enum checks on ALL
the client methods. Only do it if you know you really
need it. For most users, it is advised to use enums
to avoid errors.
'''
token = token_read_func()

Expand Down Expand Up @@ -510,4 +553,4 @@ async def oauth_client_update_token(t, *args, **kwargs):
token=token,
token_endpoint=TOKEN_ENDPOINT,
update_token=oauth_client_update_token),
token_metadata=metadata)
token_metadata=metadata, enforce_enums=enforce_enums)
Loading

0 comments on commit 53db610

Please sign in to comment.