kensho-kfinance 2.0.0__tar.gz → 2.1.2__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 kensho-kfinance might be problematic. Click here for more details.

Files changed (69) hide show
  1. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.readthedocs.yaml +6 -4
  2. {kensho_kfinance-2.0.0/kensho_kfinance.egg-info → kensho_kfinance-2.1.2}/PKG-INFO +3 -1
  3. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/build_tool_calling_documentation.py +7 -12
  4. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/conf.py +0 -6
  5. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2/kensho_kfinance.egg-info}/PKG-INFO +3 -1
  6. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/SOURCES.txt +3 -0
  7. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/requires.txt +2 -0
  8. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/CHANGELOG.md +15 -0
  9. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/batch_request_handling.py +10 -10
  10. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/constants.py +16 -7
  11. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/fetch.py +84 -40
  12. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/kfinance.py +129 -28
  13. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/meta_classes.py +9 -8
  14. kensho_kfinance-2.1.2/kfinance/tests/conftest.py +32 -0
  15. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_batch_requests.py +13 -7
  16. kensho_kfinance-2.1.2/kfinance/tests/test_client.py +54 -0
  17. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_fetch.py +8 -2
  18. kensho_kfinance-2.1.2/kfinance/tests/test_group_objects.py +32 -0
  19. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_tools.py +1 -27
  20. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_business_relationship_from_identifier.py +2 -1
  21. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_capitalization_from_identifier.py +2 -1
  22. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_company_id_from_identifier.py +2 -0
  23. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_cusip_from_ticker.py +2 -0
  24. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py +2 -0
  25. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_line_item_from_identifier.py +2 -1
  26. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_statement_from_identifier.py +2 -1
  27. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_history_metadata_from_identifier.py +2 -1
  28. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_info_from_identifier.py +2 -0
  29. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_isin_from_ticker.py +2 -0
  30. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_latest.py +2 -1
  31. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_n_quarters_ago.py +2 -1
  32. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_prices_from_identifier.py +2 -1
  33. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_security_id_from_identifier.py +2 -0
  34. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_trading_item_id_from_identifier.py +2 -0
  35. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/shared_models.py +2 -0
  36. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/version.py +2 -2
  37. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/pyproject.toml +3 -1
  38. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.coveragerc +0 -0
  39. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/ci-lint.yml +0 -0
  40. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/ci-test.yml +0 -0
  41. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/python-publish.yml +0 -0
  42. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.gitignore +0 -0
  43. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/AUTHORS.md +0 -0
  44. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/CODE_OF_CONDUCT.md +0 -0
  45. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/CONTRIBUTING.md +0 -0
  46. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/LICENSE +0 -0
  47. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/README.md +0 -0
  48. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/index.rst +0 -0
  49. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/kfinance.rst +0 -0
  50. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/requirements.txt +0 -0
  51. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/templates/apidoc/package.rst_t +0 -0
  52. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/templates/apidoc/toc.rst_t +0 -0
  53. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/tool_calling.rst +0 -0
  54. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/justfile +0 -0
  55. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/dependency_links.txt +0 -0
  56. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/top_level.txt +0 -0
  57. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/__init__.py +0 -0
  58. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/prompt.py +0 -0
  59. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/py.typed +0 -0
  60. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/server_thread.py +0 -0
  61. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/__init__.py +0 -0
  62. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_objects.py +0 -0
  63. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/README.md +0 -0
  64. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/__init__.py +0 -0
  65. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/copyright_line_check.sh +0 -0
  66. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/lint.sh +0 -0
  67. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/test.sh +0 -0
  68. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/setup.cfg +0 -0
  69. {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/setup.py +0 -0
@@ -3,13 +3,15 @@ build:
3
3
  os: ubuntu-22.04
4
4
  tools:
5
5
  python: "3.10"
6
- # You can also specify other tool versions:
7
- # nodejs: "16"
8
- # borrowed from here:
9
- # https://docs.readthedocs.com/platform/stable/build-customization.html#avoid-having-a-dirty-git-index
10
6
  jobs:
11
7
  pre_install:
8
+ # borrowed from here:
9
+ # https://docs.readthedocs.com/platform/stable/build-customization.html#avoid-having-a-dirty-git-index
12
10
  - git update-index --assume-unchanged docs/conf.py
11
+ pre_build:
12
+ # build documentation for tool calling before generating docs.
13
+ - pip install langchain-core
14
+ - python docs/build_tool_calling_documentation.py
13
15
 
14
16
  # Build documentation in the docs/ directory with Sphinx
15
17
  sphinx:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kensho-kfinance
3
- Version: 2.0.0
3
+ Version: 2.1.2
4
4
  Summary: Python CLI for kFinance
5
5
  Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
6
6
  Project-URL: source, https://github.com/kensho-technologies/kfinance
@@ -12,6 +12,7 @@ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  License-File: AUTHORS.md
15
+ Requires-Dist: cachetools<6,>=5.5
15
16
  Requires-Dist: langchain-core>=0.3.15
16
17
  Requires-Dist: langchain-google-genai<3,>=2.1.0
17
18
  Requires-Dist: numpy>=1.22.4
@@ -33,6 +34,7 @@ Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
33
34
  Requires-Dist: requests_mock<2,>=1.12; extra == "dev"
34
35
  Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
35
36
  Requires-Dist: time_machine<3,>=2.1; extra == "dev"
37
+ Requires-Dist: types-cachetools<6,>=5.5; extra == "dev"
36
38
  Dynamic: license-file
37
39
 
38
40
  # kFinance
@@ -1,5 +1,5 @@
1
- import inspect
2
1
  from enum import Enum
2
+ import inspect
3
3
  from pathlib import Path
4
4
 
5
5
  from langchain_core.utils.function_calling import convert_to_openai_tool
@@ -48,11 +48,7 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
48
48
  """
49
49
 
50
50
  # The signature built with the inspect module does not include necessary imports.
51
- imports = [
52
- "import kfinance",
53
- "import datetime",
54
- "from typing import Optional"
55
- ]
51
+ imports = ["import kfinance", "import datetime", "from typing import Optional"]
56
52
  signature_str = build_signature_str(tool)
57
53
 
58
54
  # Use inspect to retrieve the return type and add it to the imports if it's not a builtin.
@@ -66,10 +62,6 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
66
62
  for field_name, field_metadata in tool.args_schema.model_fields.items():
67
63
  # We use the openai definition to extract the field description. This means that, just like
68
64
  # pydantic/langchain, we use the docstring of an enum as the description for an enum field.
69
- try:
70
- openai_params[field_name]['description']
71
- except:
72
- assert False, tool.name
73
65
  args += f"\n :param {field_name}: {openai_params[field_name]['description']}"
74
66
  args += f"\n :type {field_name}: {display_as_type(field_metadata.annotation)}"
75
67
 
@@ -84,7 +76,8 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
84
76
  with open(inspect.getfile(tool.__class__), mode="a") as f:
85
77
  f.write(func_str)
86
78
 
87
- def build_signature_str(tool) -> str:
79
+
80
+ def build_signature_str(tool: KfinanceTool) -> str:
88
81
  """Return the signature string of the tool
89
82
 
90
83
  Return value example:
@@ -104,7 +97,6 @@ def build_signature_str(tool) -> str:
104
97
  return f"def {tool.name}{signature}:"
105
98
 
106
99
 
107
-
108
100
  def add_module_to_tool_calling_rst(tool: KfinanceTool) -> None:
109
101
  """Add a module for each tool to tool_calling.rst.
110
102
 
@@ -121,3 +113,6 @@ def add_module_to_tool_calling_rst(tool: KfinanceTool) -> None:
121
113
 
122
114
  with open(Path(Path(__file__).resolve().parent, "tool_calling.rst"), mode="a") as f:
123
115
  f.write(module_str)
116
+
117
+ if __name__ == "__main__":
118
+ add_tool_calling_docs_for_all_tools()
@@ -7,15 +7,10 @@
7
7
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
8
  from importlib.metadata import version as get_version
9
9
 
10
- from docs.build_tool_calling_documentation import add_tool_calling_docs_for_all_tools
11
-
12
-
13
10
  project = "kensho-kfinance"
14
11
  copyright = "2025, Kensho Technologies"
15
12
  author = "Kensho Technologies"
16
13
 
17
- # Add documentation for tool calling.
18
- add_tool_calling_docs_for_all_tools()
19
14
 
20
15
  # borrowed from here:
21
16
  # https://setuptools-scm.readthedocs.io/en/latest/usage/#usage-from-sphinx
@@ -59,7 +54,6 @@ templates_path = ["templates"]
59
54
  source_suffix = [".rst", ".md"]
60
55
 
61
56
 
62
-
63
57
  # -- Options for HTML output -------------------------------------------------
64
58
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
65
59
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kensho-kfinance
3
- Version: 2.0.0
3
+ Version: 2.1.2
4
4
  Summary: Python CLI for kFinance
5
5
  Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
6
6
  Project-URL: source, https://github.com/kensho-technologies/kfinance
@@ -12,6 +12,7 @@ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  License-File: AUTHORS.md
15
+ Requires-Dist: cachetools<6,>=5.5
15
16
  Requires-Dist: langchain-core>=0.3.15
16
17
  Requires-Dist: langchain-google-genai<3,>=2.1.0
17
18
  Requires-Dist: numpy>=1.22.4
@@ -33,6 +34,7 @@ Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
33
34
  Requires-Dist: requests_mock<2,>=1.12; extra == "dev"
34
35
  Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
35
36
  Requires-Dist: time_machine<3,>=2.1; extra == "dev"
37
+ Requires-Dist: types-cachetools<6,>=5.5; extra == "dev"
36
38
  Dynamic: license-file
37
39
 
38
40
  # kFinance
@@ -37,8 +37,11 @@ kfinance/py.typed
37
37
  kfinance/server_thread.py
38
38
  kfinance/version.py
39
39
  kfinance/tests/__init__.py
40
+ kfinance/tests/conftest.py
40
41
  kfinance/tests/test_batch_requests.py
42
+ kfinance/tests/test_client.py
41
43
  kfinance/tests/test_fetch.py
44
+ kfinance/tests/test_group_objects.py
42
45
  kfinance/tests/test_objects.py
43
46
  kfinance/tests/test_tools.py
44
47
  kfinance/tool_calling/README.md
@@ -1,3 +1,4 @@
1
+ cachetools<6,>=5.5
1
2
  langchain-core>=0.3.15
2
3
  langchain-google-genai<3,>=2.1.0
3
4
  numpy>=1.22.4
@@ -20,3 +21,4 @@ pytest-cov<7,>=6.0.0
20
21
  requests_mock<2,>=1.12
21
22
  ruff<1,>=0.9.4
22
23
  time_machine<3,>=2.1
24
+ types-cachetools<6,>=5.5
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.1.2
4
+ - Allow batch executor to handle multiple requests
5
+
6
+ ## v2.1.1
7
+ - Use cachetools cache
8
+
9
+ ## v2.1.0
10
+ - Filter llm tools by user permissions
11
+
12
+ ## v2.0.2
13
+ - Add Client.tickers() method for ticker industry filters for ISRCS and GICS
14
+
15
+ ## v2.0.1
16
+ - Fix readthedocs integration for llm tools.
17
+
3
18
  ## v2.0.0
4
19
  - Refactor llm tools to use langchain `BaseTool`.
5
20
 
@@ -72,19 +72,19 @@ def add_methods_of_singular_class_to_iterable_class(singular_cls: Type[T]) -> Ca
72
72
  def process_in_batch(
73
73
  method: Callable, self: IterableKfinanceClass, *args: Any, **kwargs: Any
74
74
  ) -> dict:
75
- results = {}
76
75
  kfinance_api_client = self.kfinance_api_client
77
76
  with kfinance_api_client.batch_request_header(batch_size=len(self)):
78
- with kfinance_api_client.thread_pool as executor:
79
- results = dict(
80
- zip(
81
- self,
82
- [
83
- process_in_thread_pool(executor, method, obj, *args, **kwargs)
84
- for obj in self
85
- ],
86
- )
77
+ results = dict(
78
+ zip(
79
+ self,
80
+ [
81
+ process_in_thread_pool(
82
+ kfinance_api_client.thread_pool, method, obj, *args, **kwargs
83
+ )
84
+ for obj in self
85
+ ],
87
86
  )
87
+ )
88
88
 
89
89
  return results
90
90
 
@@ -1,7 +1,6 @@
1
1
  from datetime import date
2
- from enum import Enum
3
2
  from itertools import chain
4
- from typing import TypedDict
3
+ from typing import NamedTuple, TypedDict
5
4
 
6
5
  from strenum import StrEnum
7
6
 
@@ -21,13 +20,13 @@ class HistoryMetadata(TypedDict):
21
20
  first_trade_date: date
22
21
 
23
22
 
24
- class IdentificationTriple(TypedDict):
23
+ class IdentificationTriple(NamedTuple):
25
24
  trading_item_id: int
26
25
  security_id: int
27
26
  company_id: int
28
27
 
29
28
 
30
- class Capitalization(Enum):
29
+ class Capitalization(StrEnum):
31
30
  """The capitalization type"""
32
31
 
33
32
  market_cap = "market_cap"
@@ -35,7 +34,7 @@ class Capitalization(Enum):
35
34
  shares_outstanding = "shares_outstanding"
36
35
 
37
36
 
38
- class PeriodType(Enum):
37
+ class PeriodType(StrEnum):
39
38
  """The period type"""
40
39
 
41
40
  annual = "annual"
@@ -44,7 +43,7 @@ class PeriodType(Enum):
44
43
  ytd = "ytd"
45
44
 
46
45
 
47
- class Periodicity(Enum):
46
+ class Periodicity(StrEnum):
48
47
  """The frequency or interval at which the historical data points are sampled or aggregated. Periodicity is not the same as the date range. The date range specifies the time span over which the data is retrieved, while periodicity determines how the data within that date range is aggregated."""
49
48
 
50
49
  day = "day"
@@ -53,7 +52,7 @@ class Periodicity(Enum):
53
52
  year = "year"
54
53
 
55
54
 
56
- class StatementType(Enum):
55
+ class StatementType(StrEnum):
57
56
  """The type of financial statement"""
58
57
 
59
58
  balance_sheet = "balance_sheet"
@@ -88,6 +87,16 @@ class BusinessRelationshipType(StrEnum):
88
87
  client_services = "client_services"
89
88
 
90
89
 
90
+ class Permission(StrEnum):
91
+ EarningsPermission = "EarningsPermission"
92
+ GICSPermission = "GICSPermission"
93
+ IDPermission = "IDPermission"
94
+ ISCRSPermission = "ISCRSPermission"
95
+ PricingPermission = "PricingPermission"
96
+ RelationshipPermission = "RelationshipPermission"
97
+ StatementsPermission = "StatementsPermission"
98
+
99
+
91
100
  class YearAndQuarter(TypedDict):
92
101
  year: int
93
102
  quarter: int
@@ -1,5 +1,6 @@
1
1
  from concurrent.futures import ThreadPoolExecutor
2
2
  from contextlib import contextmanager
3
+ import logging
3
4
  from time import time
4
5
  from typing import Callable, Generator, Optional
5
6
  from uuid import uuid4
@@ -13,6 +14,7 @@ from .constants import (
13
14
  IndustryClassification,
14
15
  Periodicity,
15
16
  PeriodType,
17
+ Permission,
16
18
  )
17
19
 
18
20
 
@@ -23,6 +25,8 @@ try:
23
25
  except ImportError:
24
26
  kfinance_version = "dev"
25
27
 
28
+ logger = logging.getLogger(__name__)
29
+
26
30
 
27
31
  DEFAULT_API_HOST: str = "https://kfinance.kensho.com"
28
32
  DEFAULT_API_VERSION: int = 1
@@ -86,6 +90,7 @@ class KFinanceApiClient:
86
90
  self.user_agent_source = "object_oriented"
87
91
  self._batch_id: str | None = None
88
92
  self._batch_size: str | None = None
93
+ self._user_permissions: set[Permission] | None = None
89
94
 
90
95
  @contextmanager
91
96
  def batch_request_header(self, batch_size: int) -> Generator:
@@ -127,6 +132,9 @@ class KFinanceApiClient:
127
132
  # nosemgrep: python.jwt.security.unverified-jwt-decode.unverified-jwt-decode
128
133
  options={"verify_signature": False},
129
134
  ).get("exp")
135
+ # When the access token gets refreshed, also refresh user permissions in case they
136
+ # have been updated.
137
+ self._refresh_user_permissions()
130
138
  return self._access_token
131
139
 
132
140
  def _get_access_token_via_refresh_token(self) -> str:
@@ -169,6 +177,33 @@ class KFinanceApiClient:
169
177
  response.raise_for_status()
170
178
  return response.json().get("access_token")
171
179
 
180
+ @property
181
+ def user_permissions(self) -> set[Permission]:
182
+ """Return the permissions that the current user holds."""
183
+
184
+ if self._user_permissions is None:
185
+ self._refresh_user_permissions()
186
+ # _refresh_user_permissions updates self._user_permissions in place
187
+ assert self._user_permissions is not None
188
+ return self._user_permissions
189
+
190
+ def _refresh_user_permissions(self) -> None:
191
+ """Fetches user permissions and stores them as KfinanceApiClient._user_permissions."""
192
+
193
+ user_permission_dict = self.fetch_permissions()
194
+ self._user_permissions = set()
195
+ for permission_str in user_permission_dict["permissions"]:
196
+ try:
197
+ self._user_permissions.add(Permission[permission_str])
198
+ except KeyError:
199
+ logger.warning(
200
+ "Could not resolve %s to a member of the Permission enum. "
201
+ "%s may be a new type of permission that still needs to be "
202
+ "added to the enum.",
203
+ permission_str,
204
+ permission_str,
205
+ )
206
+
172
207
  def fetch(self, url: str) -> dict:
173
208
  """Does the request and auth"""
174
209
 
@@ -191,6 +226,11 @@ class KFinanceApiClient:
191
226
  response.raise_for_status()
192
227
  return response.json()
193
228
 
229
+ def fetch_permissions(self) -> dict[str, list[str]]:
230
+ """Return the permissions of the user."""
231
+ url = f"{self.url_base}users/permissions"
232
+ return self.fetch(url)
233
+
194
234
  def fetch_id_triple(self, identifier: str, exchange_code: Optional[str] = None) -> dict:
195
235
  """Get the ID triple from [identifier]."""
196
236
  url = f"{self.url_base}id/{identifier}"
@@ -241,7 +281,7 @@ class KFinanceApiClient:
241
281
  f"{self.url_base}pricing/{trading_item_id}/"
242
282
  f"{start_date if start_date is not None else 'none'}/"
243
283
  f"{end_date if end_date is not None else 'none'}/"
244
- f"{periodicity.value if periodicity else 'none'}/"
284
+ f"{periodicity if periodicity else 'none'}/"
245
285
  f"{'adjusted' if is_adjusted else 'unadjusted'}"
246
286
  )
247
287
  return self.fetch(url)
@@ -278,7 +318,7 @@ class KFinanceApiClient:
278
318
  f"{self.url_base}price_chart/{trading_item_id}/"
279
319
  f"{start_date if start_date is not None else 'none'}/"
280
320
  f"{end_date if end_date is not None else 'none'}/"
281
- f"{periodicity.value if periodicity else 'none'}/"
321
+ f"{periodicity if periodicity else 'none'}/"
282
322
  f"{'adjusted' if is_adjusted else 'unadjusted'}"
283
323
  )
284
324
 
@@ -306,7 +346,7 @@ class KFinanceApiClient:
306
346
  """Get a specified financial statement for a specified duration."""
307
347
  url = (
308
348
  f"{self.url_base}statements/{company_id}/{statement_type}/"
309
- f"{period_type.value if period_type else 'none'}/"
349
+ f"{period_type if period_type else 'none'}/"
310
350
  f"{start_year if start_year is not None else 'none'}/"
311
351
  f"{end_year if end_year is not None else 'none'}/"
312
352
  f"{start_quarter if start_quarter is not None else 'none'}/"
@@ -327,7 +367,7 @@ class KFinanceApiClient:
327
367
  """Get a specified financial line item for a specified duration."""
328
368
  url = (
329
369
  f"{self.url_base}line_item/{company_id}/{line_item}/"
330
- f"{period_type.value if period_type else 'none'}/"
370
+ f"{period_type if period_type else 'none'}/"
331
371
  f"{start_year if start_year is not None else 'none'}/"
332
372
  f"{end_year if end_year is not None else 'none'}/"
333
373
  f"{start_quarter if start_quarter is not None else 'none'}/"
@@ -358,10 +398,12 @@ class KFinanceApiClient:
358
398
  self,
359
399
  country_iso_code: str,
360
400
  state_iso_code: Optional[str] = None,
361
- ) -> dict[str, list[IdentificationTriple]]:
401
+ ) -> list[IdentificationTriple]:
362
402
  """Fetch ticker geography groups"""
363
- return self.fetch_geography_groups(
364
- country_iso_code=country_iso_code, state_iso_code=state_iso_code, fetch_ticker=True
403
+ return self._tickers_response_to_id_triple(
404
+ self.fetch_geography_groups(
405
+ country_iso_code=country_iso_code, state_iso_code=state_iso_code, fetch_ticker=True
406
+ )
365
407
  )
366
408
 
367
409
  def fetch_company_geography_groups(
@@ -381,13 +423,13 @@ class KFinanceApiClient:
381
423
  url = f"{self.url_base}{'ticker_groups' if fetch_ticker else 'trading_item_groups'}/exchange/{exchange_code}"
382
424
  return self.fetch(url)
383
425
 
384
- def fetch_ticker_exchange_groups(
385
- self, exchange_code: str
386
- ) -> dict[str, list[IdentificationTriple]]:
426
+ def fetch_ticker_exchange_groups(self, exchange_code: str) -> list[IdentificationTriple]:
387
427
  """Fetch ticker exchange groups"""
388
- return self.fetch_exchange_groups(
389
- exchange_code=exchange_code,
390
- fetch_ticker=True,
428
+ return self._tickers_response_to_id_triple(
429
+ self.fetch_exchange_groups(
430
+ exchange_code=exchange_code,
431
+ fetch_ticker=True,
432
+ )
391
433
  )
392
434
 
393
435
  def fetch_trading_item_exchange_groups(self, exchange_code: str) -> dict[str, list[int]]:
@@ -397,13 +439,32 @@ class KFinanceApiClient:
397
439
  fetch_ticker=False,
398
440
  )
399
441
 
442
+ @staticmethod
443
+ def _tickers_response_to_id_triple(
444
+ tickers_response: dict[str, list[dict]],
445
+ ) -> list[IdentificationTriple]:
446
+ """For fetch ticker cases with a dict[str, list[dict]] response, return a list[IdentificationTriple].
447
+
448
+ For example, with a given fetch tickers response:
449
+ {"tickers" : [{"trading_item_id": 1, "security_id": 1, "company_id": 1}, {"trading_item_id": 2,"security_id": 2,"company_id": 2}]},
450
+ return [[1, 1, 1], [2, 2, 2]].
451
+ """
452
+ return [
453
+ IdentificationTriple(
454
+ trading_item_id=ticker["trading_item_id"],
455
+ security_id=ticker["security_id"],
456
+ company_id=ticker["company_id"],
457
+ )
458
+ for ticker in tickers_response["tickers"]
459
+ ]
460
+
400
461
  def fetch_ticker_combined(
401
462
  self,
402
463
  country_iso_code: Optional[str] = None,
403
464
  state_iso_code: Optional[str] = None,
404
465
  simple_industry: Optional[str] = None,
405
466
  exchange_code: Optional[str] = None,
406
- ) -> dict[str, list[IdentificationTriple]]:
467
+ ) -> list[IdentificationTriple]:
407
468
  """Fetch tickers using combined filters route"""
408
469
  if (
409
470
  country_iso_code is None
@@ -414,11 +475,11 @@ class KFinanceApiClient:
414
475
  raise RuntimeError("Invalid parameters: No parameters provided or all set to none")
415
476
  elif country_iso_code is None and state_iso_code is not None:
416
477
  raise RuntimeError(
417
- "Invalid parameters: state_iso_code must be provided with a country_iso_code value"
478
+ "Invalid parameters: country_iso_code must be provided with a state_iso_code value"
418
479
  )
419
480
  else:
420
481
  url = f"{self.url_base}ticker_groups/filters/geo/{str(country_iso_code).lower()}/{str(state_iso_code).lower()}/simple/{str(simple_industry).lower()}/exchange/{str(exchange_code).lower()}"
421
- return self.fetch(url)
482
+ return self._tickers_response_to_id_triple(self.fetch(url))
422
483
 
423
484
  def fetch_companies_from_business_relationship(
424
485
  self, company_id: int, relationship_type: BusinessRelationshipType
@@ -443,22 +504,11 @@ class KFinanceApiClient:
443
504
  url = f"{self.url_base}relationship/{company_id}/{relationship_type}"
444
505
  return self.fetch(url)
445
506
 
446
- def fetch_from_industry_code(
447
- self,
448
- industry_code: str,
449
- industry_classification: IndustryClassification,
450
- fetch_ticker: bool = True,
451
- ) -> dict[str, list]:
452
- """Fetches a list of companies or identification triples that are classified in the given industry_code and industry_classification."""
453
-
454
- url = f"{self.url_base}{'ticker_groups' if fetch_ticker else 'company_groups'}/industry/{industry_classification}/{industry_code}"
455
- return self.fetch(url)
456
-
457
507
  def fetch_ticker_from_industry_code(
458
508
  self,
459
509
  industry_code: str,
460
510
  industry_classification: IndustryClassification,
461
- ) -> dict[str, list[IdentificationTriple]]:
511
+ ) -> list[IdentificationTriple]:
462
512
  """Fetches a list of identification triples that are classified in the given industry_code and industry_classification.
463
513
 
464
514
  Returns a dictionary of shape {"tickers": List[{“company_id”: <company_id>, “security_id”: <security_id>, “trading_item_id”: <trading_item_id>}]}.
@@ -466,14 +516,11 @@ class KFinanceApiClient:
466
516
  :type industry_code: str
467
517
  :param industry_classification: The type of industry_classification to filter on.
468
518
  :type industry_classification: IndustryClassification
469
- :return: A dictionary containing the list of identification triple [company_id, security_id, trading_item_id] that are classified in the given industry_code and industry_classification.
470
- :rtype: dict[str, list[IdentificationTriple]]
519
+ :return: A list of identification triples [company_id, security_id, trading_item_id] that are classified in the given industry_code and industry_classification.
520
+ :rtype: list[IdentificationTriple]
471
521
  """
472
- return self.fetch_from_industry_code(
473
- industry_code=industry_code,
474
- industry_classification=industry_classification,
475
- fetch_ticker=True,
476
- )
522
+ url = f"{self.url_base}ticker_groups/industry/{industry_classification}/{industry_code}"
523
+ return self._tickers_response_to_id_triple(self.fetch(url))
477
524
 
478
525
  def fetch_company_from_industry_code(
479
526
  self,
@@ -490,8 +537,5 @@ class KFinanceApiClient:
490
537
  :return: A dictionary containing the list of companies that are classified in the given industry_code and industry_classification.
491
538
  :rtype: dict[str, list[int]]
492
539
  """
493
- return self.fetch_from_industry_code(
494
- industry_code=industry_code,
495
- industry_classification=industry_classification,
496
- fetch_ticker=False,
497
- )
540
+ url = f"{self.url_base}company_groups/industry/{industry_classification}/{industry_code}"
541
+ return self.fetch(url)