Skip to content

Commit

Permalink
[CIVIS-1787] DOC show details for multi-word API endpoints in Sphinx …
Browse files Browse the repository at this point in the history
…docs (#442)

* FIX multi-word endpoints show docs in sphinx

* DOC add code snippet to endpoint class docstring

* DOC misc. sphinx tweaks

* MAINT update changelog

* FIX requests site has also moved
  • Loading branch information
jacksonlee-civis committed Dec 3, 2021
1 parent 53723af commit 9c70c9b
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Ability to use joblib 1.1.x (#429)

### Fixed
- Fixed the Sphinx docs to show details of multi-word API endpoints (#442)
- Dropped the buggy/unnecessary `_get_headers` in `civis.io.read_civis_sql` (#415)
- Clarified the `table_columns` parameter in `civis.io.*` functions (#434)
- Warned about the `retry_total` parameter of `civis.APIClient` being inactive and deprecated (#431)
Expand Down
24 changes: 9 additions & 15 deletions civis/io/_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,10 +676,8 @@ def dataframe_to_civis(df, database, table, api_key=None, client=None,
required when appending to an existing table. The table_columns
parameter is required if the table does not exist, the table is being
dropped, or the columns in the source file do not appear in the same
order as in the destination table.
Example:
table_columns=[{"name": "foo", "sql_type": "INT"},
{"name": "bar", "sql_type": "VARCHAR"}]
order as in the destination table. Example:
``[{"name": "foo", "sql_type": "INT"}, {"name": "bar", "sql_type": "VARCHAR"}]``
headers : bool, optional [DEPRECATED]
Whether or not the first row of the file should be treated as
headers. The default, ``None``, attempts to autodetect whether
Expand Down Expand Up @@ -739,7 +737,7 @@ def dataframe_to_civis(df, database, table, api_key=None, client=None,
See Also
--------
:func:`~pandas.DataFrame.to_csv`
"""
""" # noqa: E501
if client is None:
client = APIClient(api_key=api_key)
if archive:
Expand Down Expand Up @@ -827,10 +825,8 @@ def csv_to_civis(filename, database, table, api_key=None, client=None,
required when appending to an existing table. The table_columns
parameter is required if the table does not exist, the table is being
dropped, or the columns in the source file do not appear in the same
order as in the destination table.
Example:
table_columns=[{"name": "foo", "sql_type": "INT"},
{"name": "bar", "sql_type": "VARCHAR"}]
order as in the destination table. Example:
``[{"name": "foo", "sql_type": "INT"}, {"name": "bar", "sql_type": "VARCHAR"}]``
delimiter : string, optional
The column delimiter. One of ``','``, ``'\\t'`` or ``'|'``.
headers : bool, optional
Expand Down Expand Up @@ -884,7 +880,7 @@ def csv_to_civis(filename, database, table, api_key=None, client=None,
... 'my-database',
... 'scratch.my_data')
>>> fut.result()
"""
""" # noqa: E501
if client is None:
client = APIClient(api_key=api_key)
if archive:
Expand Down Expand Up @@ -964,10 +960,8 @@ def civis_file_to_table(file_id, database, table, client=None,
required when appending to an existing table. The table_columns
parameter is required if the table does not exist, the table is being
dropped, or the columns in the source file do not appear in the same
order as in the destination table.
Example:
table_columns=[{"name": "foo", "sql_type": "INT"},
{"name": "bar", "sql_type": "VARCHAR"}]
order as in the destination table. Example:
``[{"name": "foo", "sql_type": "INT"}, {"name": "bar", "sql_type": "VARCHAR"}]``
primary_keys: list[str], optional
A list of the primary key column(s) of the destination table that
uniquely identify a record. These columns must not contain null values.
Expand Down Expand Up @@ -1023,7 +1017,7 @@ def civis_file_to_table(file_id, database, table, client=None,
... 'my-database',
... 'scratch.my_data')
>>> fut.result()
"""
""" # noqa: E501
if client is None:
client = APIClient()

Expand Down
20 changes: 15 additions & 5 deletions civis/resources/_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from civis.base import Endpoint, get_base_url
from civis._deprecation import deprecate_param
from civis._utils import (camel_to_snake, to_camelcase,
from civis._utils import (camel_to_snake,
open_session, get_api_key,
retry_request, MAX_RETRIES)

Expand All @@ -29,7 +29,9 @@
with open(API_SPEC_PATH) as f:
API_SPEC = json.load(f, object_pairs_hook=OrderedDict)
BASE_RESOURCES_V1 = sorted(
set(path.split("/", 2)[1] for path in API_SPEC["paths"].keys())
r for r in set(path.split("/", 2)[1] for path in API_SPEC["paths"].keys())
# "feature_flags" has a name collision with an APIClient instance
if r != "feature_flags"
)


Expand Down Expand Up @@ -467,9 +469,17 @@ def parse_api_spec(api_spec, api_version, resources):
classes = {}
for path, ops in paths.items():
base_path, methods = parse_path(path, ops, api_version, resources)
class_name = to_camelcase(base_path)
class_name = base_path.title()
if methods and classes.get(base_path) is None:
classes[base_path] = type(str(class_name), (Endpoint,), {})
cls = type(class_name, (Endpoint,), {})
cls.__doc__ = (
"Examples\n"
"--------\n"
">>> import civis\n"
">>> client = civis.APIClient()\n"
f">>> client.{base_path}.{methods[0][0]}(...)"
)
classes[base_path] = cls
for method_name, method in methods:
setattr(classes[base_path], method_name, method)
return classes
Expand Down Expand Up @@ -595,7 +605,7 @@ def _add_no_underscore_compatibility(classes):
as APIClient has a name collision with this resource. This will
be removed in v2.0.0.
"""
new = ["bocce_clusters", "match_targets", "remote_hosts", "feature_flags"]
new = ["match_targets", "remote_hosts", "feature_flags"]
classes_ = {}
class_names = list(classes.keys())
for class_name in class_names:
Expand Down
18 changes: 13 additions & 5 deletions civis/tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from jsonref import JsonRef
from requests.exceptions import HTTPError

import civis
from civis.resources import _resources, API_SPEC
from civis.resources._resources import BASE_RESOURCES_V1


RESPONSE_DOC = (
Expand Down Expand Up @@ -343,14 +345,20 @@ def test_parse_api_spec_names(mock_method):
classes = _resources.parse_api_spec(mock_api_spec, "1.0", "all")
assert sorted(classes.keys()) == ["hyphen_words", "oneword", "two_words"]
assert classes["oneword"].__name__ == "Oneword"
assert classes["two_words"].__name__ == "TwoWords"
assert classes["hyphen_words"].__name__ == "HyphenWords"
assert classes["two_words"].__name__ == "Two_Words"
assert classes["hyphen_words"].__name__ == "Hyphen_Words"


def test_add_no_underscore_compatibility():
classes = dict(bocce_clusters=1,
classes = dict(match_targets=1,
feature_flags=2)
new_classes = _resources._add_no_underscore_compatibility(classes)
assert new_classes["bocceclusters"] == 1
assert new_classes["bocce_clusters"] == 1
assert new_classes["matchtargets"] == 1
assert new_classes["match_targets"] == 1
assert new_classes.get("feature_flags") is None


def test_endpoints_from_base_resources_are_available_from_client():
client = civis.APIClient(local_api_spec=API_SPEC, api_key="none")
for endpoint in BASE_RESOURCES_V1:
assert hasattr(client, endpoint), endpoint
10 changes: 5 additions & 5 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
autosummary_generate = True

intersphinx_mapping = {
'pandas': ('http://pandas.pydata.org/pandas-docs/stable', None),
'pandas': ('https://pandas.pydata.org/pandas-docs/stable', None),
'python': ('https://docs.python.org/3', None),
'requests': ('https://requests.readthedocs.io/en/latest/', None),
'sklearn': ('http://scikit-learn.org/stable', None),
'requests': ('https://docs.python-requests.org/en/latest/', None),
'sklearn': ('https://scikit-learn.org/stable', None),
'joblib': ('https://joblib.readthedocs.io/en/latest/', None),
}

Expand All @@ -57,7 +57,7 @@

# General information about the project.
current_year = datetime.datetime.now().year
project = 'Civis Client'
project = 'Civis API Python Client'
copyright = '2016-%d, Civis Analytics' % current_year
author = 'Civis Analytics'

Expand Down Expand Up @@ -349,7 +349,7 @@ def _attach_classes_to_module(module, class_data):

def _write_resources_rst(class_names, filename, civis_module):
with open(filename, 'w') as _out:
_out.write('API Resources\n=============\n\n')
_out.write('.. _api_resources:\n\nAPI Resources\n=============\n\n')
for class_name in class_names:
name = class_name.title()
header = '`{}`\n{}\n'.format(name, '"' * (len(name) + 2))
Expand Down
2 changes: 2 additions & 0 deletions docs/source/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ your user information:
'name': 'User Name',
'username': 'uname'}
For a complete list of the API endpoints and their methods,
check out :ref:`api_resources`.

Suppose we did not have the ``civis.io`` namespace. This is how we might export
a CSV file from Civis. As you will see, this can be quite involved and the
Expand Down

0 comments on commit 9c70c9b

Please sign in to comment.