name: tup
description: Alternative for tuple that does not collapse nor need a comma when at 1 val and operates within guide lines of tuple. Intentionally left it as
near tuple usage as possible but could add add remove append methods and instance return tuple state.
reason: Python has so rewarding and found this to be very useful for myself so shared here, also if doing code review that means someone unlike me being
a noob could make even more better probally so I am tossing it here.
how to use: from tupled import tup at top of source file. assign name = tup()
edit: I am on Linux so if on windows just thought of may run into write permissions, should be fine on both but if you so can change line to this:
REPLACE: TUP_FOLDER = ".tup"
um WITH THIS: TUP_FOLDER = os.path.join(os.path.expanduser("~"), ".tup")
description: Alternative for tuple that does not collapse nor need a comma when at 1 val and operates within guide lines of tuple. Intentionally left it as
near tuple usage as possible but could add add remove append methods and instance return tuple state.
reason: Python has so rewarding and found this to be very useful for myself so shared here, also if doing code review that means someone unlike me being
a noob could make even more better probally so I am tossing it here.
how to use: from tupled import tup at top of source file. assign name = tup()
edit: I am on Linux so if on windows just thought of may run into write permissions, should be fine on both but if you so can change line to this:
REPLACE: TUP_FOLDER = ".tup"
um WITH THIS: TUP_FOLDER = os.path.join(os.path.expanduser("~"), ".tup")
# tupled.py — smart, auto-promoting persistent container that mimics tuple behavior
import os
TUP_FOLDER = ".tup"
def clear_all_tups_on_launch():
if os.path.exists(TUP_FOLDER):
for file in os.listdir(TUP_FOLDER):
path = os.path.join(TUP_FOLDER, file)
if os.path.isfile(path):
os.remove(path)
clear_all_tups_on_launch()
class tup:
def __init__(self, *initial_values, name=None):
self.name = name
self.value = None
if initial_values:
self._add(*initial_values)
elif self.name:
self._load()
def __call__(self, *args):
if not args:
return self.get()
return self._add(*args)
def get(self):
if isinstance(self.value, tuple):
return self.value
elif self.value is not None:
return (self.value,)
return None
def _add(self, *args):
if not args:
return self.get()
if self.value is None:
self.value = args[0] if len(args) == 1 else args
elif isinstance(self.value, tuple):
self.value += args
else:
self.value = (self.value,) + args
self._write()
return self.get()
def _remove(self, item):
if self.value is None:
return self.get()
current = self.get()
if isinstance(current, tuple):
filtered = tuple(x for x in current if x != item)
if len(filtered) == 0:
self.value = None
else:
self.value = filtered[0] if len(filtered) == 1 else filtered
elif current == item:
self.value = None
self._write()
return self.get()
def _write(self):
if not self.name:
return # Skip writing if no name set
os.makedirs(TUP_FOLDER, exist_ok=True)
path = os.path.join(TUP_FOLDER, self.name)
if self.value is None:
if os.path.exists(path):
os.remove(path)
return
with open(path, "w") as f:
for v in self.get():
f.write(f"{repr(v)}\n")
def __add__(self, other):
new = tup(name=None) # don't persist this by default
new.value = self.get()
if new.value is None:
new.value = (other,)
else:
new.value += (other,)
return new
def __iadd__(self, other):
if self.value is None:
self.value = (other,)
else:
self.value += (other,)
self._write()
return self
def _load(self):
path = os.path.join(TUP_FOLDER, self.name)
if os.path.exists(path):
with open(path, "r") as f:
lines = [eval(line.strip()) for line in f if line.strip()]
if len(lines) == 1:
self.value = lines[0]
elif lines:
self.value = tuple(lines)
def __iter__(self):
return iter(self.get())
def __getitem__(self, index):
result = self.get()[index]
if isinstance(index, slice):
if isinstance(result, tuple) and len(result) == 1:
return result[0] # return bare value
return result
return result
def __len__(self):
val = self.get()
return len(val) if val is not None else 0
def __contains__(self, item):
return item in self.get()
def __eq__(self, other):
return self.get() == other
def __getattr__(self, attr):
# Avoid intercepting real class methods like 'add', 'remove', etc.
if attr in self.__class__.__dict__:
raise AttributeError(f"'tup' object has no attribute '{attr}'")
val = self.get()
if val is None:
raise AttributeError(f"'tup' object has no value yet, so it has no attribute '{attr}'")
return getattr(val, attr)
def as_tuple(self):
return self.get()
@classmethod
def from_tuple(cls, name, tup_data):
t = cls(name)
t(*tup_data)
return t
def __repr__(self):
val = self.get()
if val is None:
return f"tup({self.name!r})"
elif isinstance(val, tuple):
if len(val) == 1:
return f"({repr(val[0])})" # No comma!
else:
return repr(val)
def as_list(self):
return list(self.get() or [])
def as_set(self):
return set(self.get() or [])
def as_dict(self):
return dict(self.get() or [])[python]
Larz60+ write Jul-20-2025, 03:56 AM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Tags have been added this time, please use on future posts.
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Tags have been added this time, please use on future posts.
Attached Files
