From da94e8b9771864dfd4eb7de06550e5f4904dbff2 Mon Sep 17 00:00:00 2001 From: atalha1 <223329331+atalha1@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:43:47 +0300 Subject: [PATCH] fix CAPEX shortcut collision in names.py, guard generator against it CAPEX was getting silently redefined. names.py has: CAPEX = 'Capital Expenditures' ... CAPEX = CHG_FIX_ASSETS_INT = 'Change in Fixed Assets & Intangibles' since the file is written in alphabetical order by column name, the second line runs later and CAPEX ends up meaning 'Change in Fixed Assets & Intangibles' instead of 'Capital Expenditures', with no error, just wrong data for anyone using sf.CAPEX. root cause is in tools/generate_names.py: it dedupes shortcuts within a single column-name but never checks whether the same shortcut is attached to two different names on the server side. added that check, if a shortcut is claimed by more than one name it gets skipped for both (with a clear stderr warning) instead of one silently winning. figured skipping and warning is safer than guessing which name should keep it since I can't verify that against real data. also manually fixed the specific CAPEX line in the currently shipped names.py, since that part doesn't get regenerated until someone reruns the tool against the live server. fixes #10 --- simfin/names.py | 2 +- tools/generate_names.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/simfin/names.py b/simfin/names.py index 69b9def..9f4afcf 100644 --- a/simfin/names.py +++ b/simfin/names.py @@ -87,7 +87,7 @@ CHG_CASH_DISCOP_OTHER = 'Change in Cash from Disc. Operations and Other' -CAPEX = CHG_FIX_ASSETS_INT = 'Change in Fixed Assets & Intangibles' +CHG_FIX_ASSETS_INT = 'Change in Fixed Assets & Intangibles' CHG_INSURANCE_RESERVES = 'Change in Insurance Reserves' diff --git a/tools/generate_names.py b/tools/generate_names.py index 3f51c01..cbb2bbb 100644 --- a/tools/generate_names.py +++ b/tools/generate_names.py @@ -171,6 +171,30 @@ def _process(): # At this point the entire json-file has been organized and we now # want to write the data to the names.py file. + # Find shortcuts that are claimed by more than one distinct column-name + # (e.g. server-side 'CAPEX' attached to both 'Capital Expenditures' and + # 'Change in Fixed Assets & Intangibles'). If we don't guard against + # this, names.py ends up with two separate "SHORTCUT = '...'" lines and + # whichever one comes later in alphabetical order silently overwrites + # the other at import time, so the shortcut ends up pointing at the + # wrong column with no error or warning (see issue #10). We skip these + # shortcuts entirely rather than guess which name should win. + shortcut_to_names = {} + for name, record_org in data_organized.items(): + for shortcut in set(record_org['shortcuts']): + shortcut_to_names.setdefault(shortcut, set()).add(name) + conflicting_shortcuts = {shortcut for shortcut, names in shortcut_to_names.items() + if len(names) > 1} + + if len(conflicting_shortcuts) > 0: + msg = 'Shortcuts claimed by more than one column-name (skipped, needs a ' \ + 'fix upstream on the SimFin server): {}'.format(len(conflicting_shortcuts)) + print(msg, file=sys.stderr) + for shortcut in sorted(conflicting_shortcuts): + names = sorted(shortcut_to_names[shortcut]) + print('- {}: {}'.format(shortcut, names), file=sys.stderr) + print('', file=sys.stderr) + # Text-wrapper to ensure comment-lines are kept within the given length. # Note that #: is used by sphinx autodoc to create docs for a variable. wrapper = TextWrapper(width=80, @@ -212,8 +236,9 @@ def _process(): for line in desc_lines: print(line, file=file) - # Ensure the shortcuts are unique for this name. - shortcuts = list(set(shortcuts)) + # Ensure the shortcuts are unique for this name, and drop any + # that are also claimed by a different column-name. + shortcuts = list(set(shortcuts) - conflicting_shortcuts) # Write shortcut(s) to file e.g. 'CLOSE = SHARE_PRICE = ' # This will be sorted ascendingly.