Skip to content

Commit

Permalink
Commented py4cytoscape_utils to capture all cases
Browse files Browse the repository at this point in the history
  • Loading branch information
bdemchak committed Sep 29, 2024
1 parent da04b7f commit 0c30f51
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
26 changes: 20 additions & 6 deletions py4cytoscape/py4cytoscape_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,15 +853,21 @@ def verify_supported_versions(cyrest=1, cytoscape=3.6, base_url=DEFAULT_BASE_URL


def normalize_list(entity):
# Return a Python list of strings given a Python list, a string list of strings, a string list of ints, or a scalar
# Try to return a list representing entity, but the actual return value depends on the entity passed in
# If the entity is str, it could contain a single value or a comma-separated list of values
# if all of these values look like integers, the return value is a python list of integers (e.g., '1,2,3' -> [1,2,3] or '1'->[1])
# if at least one value doesn't look like an integer, the return value is a python list of strings (e.g., 'A,B,1' -> ['A','B','1'] or 'A' -> ['A'])
# If the entity is a list, the list is just returned as is (e.g., ['A','B','C'] -> ['A','B','C'] or [1,2,3] -> [1,2,3])
# Note that this could defeat the intention of normalization if the list contains values of mixed
# type (e.g., [1, 'A']) that are likely an error. But we let the caller detect this error.
# If the entity is neither list nor string, it is returned as a python list of length 1 (e.g., 123 -> [123]
if isinstance(entity, str): # If it's a string, it could be names, SUIDs, or whatever
scalar_list = str.split(entity, ',')
try:
return [int(x) for x in scalar_list] # for each entry, see if it's a number
except:
return [str.strip(x) for x in scalar_list] # for each entry, get rid of leading/trialing spaces
elif not isinstance(entity, list): # If it's not a string, it could be int, int64 or whatever
# Note that list could contain strings with leading/trailing spaces. Caller will likely think this is an error.
return [entity]
else:
return entity
Expand Down Expand Up @@ -950,22 +956,30 @@ def _item_to_suid(item_names, table_name, network=None, base_url=DEFAULT_BASE_UR
if item_names is None:
return None

# Normalize input item names
# Normalize input item names ... item_names could come back as a list of name ints (if all items look
# like ints) or strings (if at least one name isn't an int). Normal situation is list of strings, but
# list of ints is legitimate if the input names are already a list of SUIDs or if the table's name
# column contains names that look like ints.
item_names = normalize_list(item_names)

# Get a table of column names indexed by SUID and a set containing all of the SUIDs
df = tables.get_table_columns(table_name, ['name'], 'default', network, base_url=base_url)
all_suids = set(df.index)

# Check if all item names are valid SUIDs, return immediately if true
# (... fails if any item is a string or a number that's not a SUID but could be an item name)
if all(item_name in all_suids for item_name in item_names):
return item_names

# Map all names to SUIDs for O(1) lookup, allowing multiple SUIDs per name
item_name_to_suid_list = {}
for suid, name in zip(df.index, df['name']):
for suid, name in zip(df.index, df['name']): # loop through all known SUIDS/names
try:
name = int(name) # Attempt to convert the name to an integer.
name = int(name) # name looks like a number ... important because item_names will contain number if item looks like a number
except ValueError:
pass # If conversion fails, keep the name as a string.
pass # name looks like a string ... use it as is

# Add name and SUID(s) to map
if name not in item_name_to_suid_list:
item_name_to_suid_list[name] = [] # Initialize the key with an empty list if it doesn't exist.
item_name_to_suid_list[name].append(suid) # Add the suid to the list associated with the name.
Expand Down
1 change: 1 addition & 0 deletions tests/test_py4cytoscape_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def test_node_name_to_node_suid(self):
self.assertRaises(CyError, node_name_to_node_suid, names_with_none) # try bad node SUID
self.assertEqual(node_name_to_node_suid(suids_str), suids) # try string list of node SUIDs
self.assertEqual(node_name_to_node_suid(suids[0]), [suids[0]]) # try just a single node SUID
self.assertEqual(node_name_to_node_suid(str(suids[0])), [suids[0]]) # try just a single node SUID as string

# Verify that when there are two of the same-named nodes, one of their SUIDs is returned if one node is queried
suid_orig = index[values.index('YGR009C')]
Expand Down

1 comment on commit 0c30f51

@GeneCodeSavvy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdemchak Looks good to me !

Please sign in to comment.