kensho-kfinance 2.2.4__py3-none-any.whl → 2.2.5__py3-none-any.whl

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kensho-kfinance
3
- Version: 2.2.4
3
+ Version: 2.2.5
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
@@ -1,22 +1,23 @@
1
- kensho_kfinance-2.2.4.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
2
- kensho_kfinance-2.2.4.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
3
- kfinance/CHANGELOG.md,sha256=rBjrg8H9eODymBNuLyOWIRrA5FAVR92WgxswFMkGI9E,1241
1
+ kensho_kfinance-2.2.5.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
2
+ kensho_kfinance-2.2.5.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
3
+ kfinance/CHANGELOG.md,sha256=Nlkx-7DFWd9kyeKUdm7pqHW7bFi8ZdruQxCmOo88TpY,1302
4
4
  kfinance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  kfinance/batch_request_handling.py,sha256=p6p_G4_BL06GgeKlh7P1k9CUqOMahWCLEw1NoBwbLvU,5698
6
6
  kfinance/constants.py,sha256=UuFzqL253-2tRQfma785K9tfaZGv-o821tO2tVLwc5Q,48813
7
- kfinance/fetch.py,sha256=fguxYy5rjLQKNkmP7gsvZUJtmt9uDNxg6LN9FRJTAVQ,22936
7
+ kfinance/fetch.py,sha256=y4B-xPKrGNDVkicSf904q-M1hXHtNN8AuJ0Y55x_r2s,23464
8
8
  kfinance/kfinance.py,sha256=_U69k0Dcwkw1B_lzaUZy8N2-c-v93ZKoUAVVZb6wBUM,52758
9
- kfinance/meta_classes.py,sha256=1qYkj2L7jcBfdkye6TG2nuRhwzhAbmtxay6rSVY3DsA,19883
9
+ kfinance/meta_classes.py,sha256=3V0nSXDDoake5o7kXnrqXuqNIiwI75KR4IYxFqSPhTE,20736
10
10
  kfinance/prompt.py,sha256=PtVB8c_FcSlVdyGgByAnIFGzuUuBaEjciCqnBJl1hSQ,25133
11
11
  kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ kfinance/pydantic_models.py,sha256=WWLjcxBvVOpW2Wzpq1zKKju4uMlIeH4nKvM2GqLsjEE,597
12
13
  kfinance/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
13
- kfinance/version.py,sha256=Ath1yNFbHtPzluPba0J7EfQTfLH_byRzLfGjqAUl6cY,511
14
+ kfinance/version.py,sha256=VXEVDzLsFHRu1B1J9TV6pbjt3sWduv6BDj43YpV5xIM,511
14
15
  kfinance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  kfinance/tests/conftest.py,sha256=voB-w8P_6L3Nel3rdgylXKe5WWaS1q7nCFt1O04uqoY,948
16
17
  kfinance/tests/test_batch_requests.py,sha256=uXJF2IcRdyBm5SthwIUHMKtkGZ21MY84pg_k1JeSNOY,11430
17
18
  kfinance/tests/test_client.py,sha256=O7icZCSDhlQ9WGhzoXlpiSvbuA-mQNJHBYVsilyP_dE,2209
18
19
  kfinance/tests/test_example_notebook.py,sha256=XhDAJp3H5Y6usLAt3k4Ug4W3H6gLyh68nzmoWgOOLn4,6441
19
- kfinance/tests/test_fetch.py,sha256=O9qOsffQYJPSAR1X_P6CNoA0Buyq2kx4vfS0FOc2Goc,13564
20
+ kfinance/tests/test_fetch.py,sha256=LbE8JLS4OsByfQ_GsGxYwSX5Z4Dgfr-T6bFaZo1k7Oo,16079
20
21
  kfinance/tests/test_group_objects.py,sha256=SoMEZmkG4RYdgWOAwxLHHtzIQho92KM01YbQXPUg578,1689
21
22
  kfinance/tests/test_objects.py,sha256=CSk3iN-uDt-E6gaOX4jExuYPT8Up-YNC3hafpenadcA,23614
22
23
  kfinance/tests/test_tools.py,sha256=ne4XhebhoxDHFl4kRLl10j4K-SXLrxS6jv6Ypt0Gv_E,16846
@@ -37,7 +38,7 @@ kfinance/tool_calling/get_prices_from_identifier.py,sha256=ViJkwLDvStB7grc8RuoKS
37
38
  kfinance/tool_calling/get_segments_from_identifier.py,sha256=WIqJ1wWE6Z87VBREGu42nRc6_eJqUbGKcE9elzqBQJE,1867
38
39
  kfinance/tool_calling/resolve_identifier.py,sha256=npslr6bBCu0qEDV1-8d24F5OC3nQ1KBMphuMbHVC1AU,626
39
40
  kfinance/tool_calling/shared_models.py,sha256=K-NPQyE_7Ew6Cs0zxG1xO2O47gp5uDHdHtWD7wUDZX4,2132
40
- kensho_kfinance-2.2.4.dist-info/METADATA,sha256=sx_SF7gbkLwRfyTRUw2WHaqkoSyqQfUoBUS5majgF7w,3436
41
- kensho_kfinance-2.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
- kensho_kfinance-2.2.4.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
43
- kensho_kfinance-2.2.4.dist-info/RECORD,,
41
+ kensho_kfinance-2.2.5.dist-info/METADATA,sha256=ilytuQ2DVw1h-i2Qo3EV-VKP6_p0OgiuF-Fzdvo5R9M,3436
42
+ kensho_kfinance-2.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
+ kensho_kfinance-2.2.5.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
44
+ kensho_kfinance-2.2.5.dist-info/RECORD,,
kfinance/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.2.5
4
+ - Add parsing for relationship response with name
5
+
3
6
  ## v2.2.4
4
7
  - Add segments as llm tools
5
8
 
kfinance/fetch.py CHANGED
@@ -6,6 +6,7 @@ from typing import Callable, Generator, Optional
6
6
  from uuid import uuid4
7
7
 
8
8
  import jwt
9
+ from pydantic import ValidationError
9
10
  import requests
10
11
 
11
12
  from .constants import (
@@ -17,6 +18,7 @@ from .constants import (
17
18
  Permission,
18
19
  SegmentType,
19
20
  )
21
+ from .pydantic_models import RelationshipResponse, RelationshipResponseNoName
20
22
 
21
23
 
22
24
  # version.py gets autogenerated by setuptools-scm and is not available
@@ -505,26 +507,30 @@ class KFinanceApiClient:
505
507
 
506
508
  def fetch_companies_from_business_relationship(
507
509
  self, company_id: int, relationship_type: BusinessRelationshipType
508
- ) -> dict[str, list[int]]:
509
- """Fetches a dictionary of current and previous company IDs associated with a given company ID based on the specified relationship type.
510
-
511
- The returned dictionary has the following structure:
512
- {
513
- "current": List[int],
514
- "previous": List[int]
515
- }
510
+ ) -> RelationshipResponse | RelationshipResponseNoName:
511
+ """Fetches a dictionary of current and previous company IDs and names associated with a given company ID based on the specified relationship type.
516
512
 
517
513
  Example: fetch_companies_from_business_relationship(company_id=1234, relationship_type="distributor") returns a dictionary of company 1234's current and previous distributors.
518
514
 
515
+ As of 2024-05-28, we are changing the response on the backend from
516
+ RelationshipResponseNoName to RelationshipResponse. This function can handle both response
517
+ types.
518
+
519
519
  :param company_id: The ID of the company for which associated companies are being fetched.
520
520
  :type company_id: int
521
521
  :param relationship_type: The type of relationship to filter by. Valid relationship types are defined in the BusinessRelationshipType class.
522
522
  :type relationship_type: BusinessRelationshipType
523
523
  :return: A dictionary containing lists of current and previous company IDs that have the specified relationship with the given company_id.
524
- :rtype: dict[str, list[int]]
524
+ :rtype: RelationshipResponse | RelationshipResponseNoName
525
525
  """
526
526
  url = f"{self.url_base}relationship/{company_id}/{relationship_type}"
527
- return self.fetch(url)
527
+ result = self.fetch(url)
528
+ # Try to parse as the newer RelationshipResponse and fall back to
529
+ # RelationshipResponseNoName if that fails.
530
+ try:
531
+ return RelationshipResponse.model_validate(result)
532
+ except ValidationError:
533
+ return RelationshipResponseNoName.model_validate(result)
528
534
 
529
535
  def fetch_ticker_from_industry_code(
530
536
  self,
kfinance/meta_classes.py CHANGED
@@ -9,12 +9,12 @@ import pandas as pd
9
9
 
10
10
  from .constants import LINE_ITEMS, BusinessRelationshipType, PeriodType, SegmentType
11
11
  from .fetch import KFinanceApiClient
12
+ from .pydantic_models import RelationshipResponse
12
13
 
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from .kfinance import BusinessRelationships
16
17
 
17
-
18
18
  logger = logging.getLogger(__name__)
19
19
 
20
20
 
@@ -223,14 +223,32 @@ class CompanyFunctionsMetaClass:
223
223
  """
224
224
  from .kfinance import BusinessRelationships, Companies
225
225
 
226
- companies = self.kfinance_api_client.fetch_companies_from_business_relationship(
227
- self.company_id,
228
- relationship_type,
229
- )
230
- return BusinessRelationships(
231
- Companies(self.kfinance_api_client, companies["current"]),
232
- Companies(self.kfinance_api_client, companies["previous"]),
226
+ relationship_resp = self.kfinance_api_client.fetch_companies_from_business_relationship(
227
+ company_id=self.company_id,
228
+ relationship_type=relationship_type,
233
229
  )
230
+ if isinstance(relationship_resp, RelationshipResponse):
231
+ return BusinessRelationships(
232
+ current=Companies(
233
+ kfinance_api_client=self.kfinance_api_client,
234
+ company_ids=[c.company_id for c in relationship_resp.current],
235
+ ),
236
+ previous=Companies(
237
+ kfinance_api_client=self.kfinance_api_client,
238
+ company_ids=[c.company_id for c in relationship_resp.previous],
239
+ ),
240
+ )
241
+ else:
242
+ return BusinessRelationships(
243
+ current=Companies(
244
+ kfinance_api_client=self.kfinance_api_client,
245
+ company_ids=relationship_resp.current,
246
+ ),
247
+ previous=Companies(
248
+ kfinance_api_client=self.kfinance_api_client,
249
+ company_ids=relationship_resp.previous,
250
+ ),
251
+ )
234
252
 
235
253
  def market_cap(
236
254
  self,
@@ -0,0 +1,25 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class RelationshipResponseNoName(BaseModel):
5
+ """A response from the relationship endpoint before adding the company name.
6
+
7
+ Each element in `current` and `previous` is a company_id.
8
+ """
9
+
10
+ current: list[int]
11
+ previous: list[int]
12
+
13
+
14
+ class CompanyIdAndName(BaseModel):
15
+ """A company_id and name"""
16
+
17
+ company_id: int
18
+ company_name: str
19
+
20
+
21
+ class RelationshipResponse(BaseModel):
22
+ """A response from the relationship endpoint that includes both company_id and name."""
23
+
24
+ current: list[CompanyIdAndName]
25
+ previous: list[CompanyIdAndName]
@@ -2,9 +2,17 @@ from unittest import TestCase
2
2
  from unittest.mock import MagicMock
3
3
 
4
4
  import pytest
5
+ from requests_mock import Mocker
5
6
 
6
- from kfinance.constants import Periodicity, PeriodType
7
+ from kfinance.constants import BusinessRelationshipType, Periodicity, PeriodType
7
8
  from kfinance.fetch import KFinanceApiClient
9
+ from kfinance.kfinance import Client
10
+ from kfinance.pydantic_models import (
11
+ CompanyIdAndName,
12
+ RelationshipResponse,
13
+ RelationshipResponseNoName,
14
+ )
15
+ from kfinance.tests.conftest import SPGI_COMPANY_ID
8
16
 
9
17
 
10
18
  def build_mock_api_client() -> KFinanceApiClient:
@@ -279,3 +287,56 @@ class TestMarketCap:
279
287
  expected_fetch_url = f"{client.url_base}users/permissions"
280
288
  client.fetch_permissions()
281
289
  client.fetch.assert_called_with(expected_fetch_url)
290
+
291
+
292
+ class TestFetchCompaniesFromBusinessRelationship:
293
+ def test_old_response_format(self, requests_mock: Mocker, mock_client: Client) -> None:
294
+ """
295
+ GIVEN a business relationship request
296
+ WHEN the api returns a response in the old (no name) format
297
+ THEN the response can successfully be parsed.
298
+ """
299
+ http_resp = {"current": [883103], "previous": [472898, 8182358]}
300
+ expected_result = RelationshipResponseNoName(current=[883103], previous=[472898, 8182358])
301
+ requests_mock.get(
302
+ url=f"{mock_client.kfinance_api_client.url_base}relationship/{SPGI_COMPANY_ID}/{BusinessRelationshipType.supplier}",
303
+ json=http_resp,
304
+ )
305
+
306
+ resp = mock_client.kfinance_api_client.fetch_companies_from_business_relationship(
307
+ company_id=SPGI_COMPANY_ID, relationship_type=BusinessRelationshipType.supplier
308
+ )
309
+ assert resp == expected_result
310
+
311
+ def test_new_response_format(self, requests_mock: Mocker, mock_client: Client) -> None:
312
+ """
313
+ GIVEN a business relationship request
314
+ WHEN the api returns a response in the new (with name) format
315
+ THEN the response can successfully be parsed.
316
+ """
317
+
318
+ http_resp = {
319
+ "current": [{"company_name": "foo", "company_id": 883103}],
320
+ "previous": [
321
+ {"company_name": "bar", "company_id": 472898},
322
+ {"company_name": "baz", "company_id": 8182358},
323
+ ],
324
+ }
325
+
326
+ expected_result = RelationshipResponse(
327
+ current=[CompanyIdAndName(company_name="foo", company_id=883103)],
328
+ previous=[
329
+ CompanyIdAndName(company_name="bar", company_id=472898),
330
+ CompanyIdAndName(company_name="baz", company_id=8182358),
331
+ ],
332
+ )
333
+
334
+ requests_mock.get(
335
+ url=f"{mock_client.kfinance_api_client.url_base}relationship/{SPGI_COMPANY_ID}/{BusinessRelationshipType.supplier}",
336
+ json=http_resp,
337
+ )
338
+
339
+ resp = mock_client.kfinance_api_client.fetch_companies_from_business_relationship(
340
+ company_id=SPGI_COMPANY_ID, relationship_type=BusinessRelationshipType.supplier
341
+ )
342
+ assert resp == expected_result
kfinance/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.2.4'
21
- __version_tuple__ = version_tuple = (2, 2, 4)
20
+ __version__ = version = '2.2.5'
21
+ __version_tuple__ = version_tuple = (2, 2, 5)