def get_context(self): ret = {} ret['sysconfig'] = glom(sysconfig, T.get_config_vars(), skip_exc=Exception) ret['paths'] = glom(sysconfig, T.get_paths(), skip_exc=Exception) return ret
def get_proc_info(): ret = {} ret['pid'] = os.getpid() _user_t, _sys_t = os.times()[:2] ret['cpu_times'] = {'user_time': _user_t, 'sys_time': _sys_t} ret['cwd'] = os.getcwdu() ret['umask'] = os.umask(os.umask(2)) # have to set to get ret['umask_str'] = '{0:03o}'.format(ret['umask']) ret['owner'] = glom(globals(), Coalesce(T['getpass'].getuser(), T['os'].getuid()), skip_exc=Exception) # use 0 to get current niceness, seems to return process group's nice level unix_only_vals = glom(os, { 'ppid': T.getppid(), 'pgid': T.getpgrp(), 'niceness': T.nice(0) }, skip_exc=AttributeError) ret.update(unix_only_vals) ret['rusage'] = get_rusage_dict() ret['rlimit'] = get_rlimit_dict() return ret
def test_path_t_roundtrip(): # check that T repr roundrips assert repr(T['a'].b.c()) == "T['a'].b.c()" assert repr(T[1:]) == "T[1:]" assert repr(T[::3, 1:, 1:2, :2:3]) == "T[::3, 1:, 1:2, :2:3]" # check that Path repr roundtrips assert repr(Path('a', 1, 'b.b', -1.0)) == "Path('a', 1, 'b.b', -1.0)" # check that Path repr roundtrips when it contains Ts assert repr(Path(T['a'].b, 'c', T['d'].e)) == "Path(T['a'].b, 'c', T['d'].e)" # check that T instances containing Path access revert to repring with Path assert repr(Path(T['a'].b, 'c', T['d'].e).path_t) == "Path(T['a'].b, 'c', T['d'].e)" # check that Paths containing only T objects reduce to a T (joining the T objects) assert repr(Path(T['a'].b, T.c())) == "T['a'].b.c()" # check that multiple nested paths reduce assert repr(Path(Path(Path('a')))) == "Path('a')" # check builtin repr assert repr(T[len]) == 'T[len]' assert repr(T.func(len, sum)) == 'T.func(len, sum)'
def get_gc_info(): import gc ret = {} ret['is_enabled'] = gc.isenabled() ret['thresholds'] = gc.get_threshold() ret['counts'] = glom(gc, T.get_count(), skip_exc=Exception) ret['obj_count'] = glom(gc, T.get_objects().__len__(), skip_exc=Exception) return ret
def get_gc_info(): import gc ret = {} ret['is_enabled'] = gc.isenabled() ret['thresholds'] = gc.get_threshold() ret['counts'] = glom(gc, T.get_count(), skip_exc=Exception) ret['obj_count'] = glom(gc, (T.get_objects(), len), skip_exc=Exception) return ret
def _spec_to_type( key: str, value: Dict[str, Dict], bases: Tuple[Type, ...] = ()) -> Type: """Using the type specification, create the custom type objects Parameters ---------- key : str The key name corresponding to the specification. It is used as a template for the custom type name. value : Dict The dictionary with the type specification. It looks like: { "key1": {"type": <type1>, "validator": <validator1>}, "key2": {"type": <type2>, "validator": <validator2>}, # ... } bases : Tuple[Type, ...] Base classes Returns ------- Type Custom type object with validators """ type_k = _type_spec[0] dflt_k = _type_spec[6] fields = glom( # NOTE: original ordering is preserved, apart from moving the data # members w/ default arguments later. [(k, v) for k, v in value.items() if type_k in v and dflt_k not in v] + [(k, v) for k, v in value.items() if type_k in v and dflt_k in v], [( { "k": "0", "v": f"1.{type_k}", # TODO: non-trivial defaults like mutable types "d": Coalesce(f"1.{dflt_k}", default=SKIP), }, T.values(), tuple, )], ) # extract key, value and convert to list of tuples ns = dict( chain(*glom( value.values(), [(Coalesce("validator", default_factory=dict), T.items())], ))) # chain dict.items() and create namespace return make_typedconfig(f"{key}_t", fields, namespace=ns, bases=bases)
def test_call_and_target(): class F(object): def __init__(s, a, b, c): s.a, s.b, s.c = a, b, c call_f = Call(F, kwargs=dict(a=T, b=T, c=T)) assert repr(call_f) val = glom(1, call_f) assert (val.a, val.b, val.c) == (1, 1, 1) class F(object): def __init__(s, a): s.a = a val = glom({'one': F('two')}, Call(F, args=(T['one'].a, ))) assert val.a == 'two' assert glom({'a': 1}, Call(F, kwargs=T)).a == 1 assert glom([1], Call(F, args=T)).a == 1 assert glom(F, T(T)).a == F assert glom([F, 1], T[0](T[1]).a) == 1 assert glom([[1]], T[0][0][0][UP]) == 1 assert glom([[1]], T[0][UP][UP][UP]) == [[1]] # tops out at just T assert list(glom({'a': 'b'}, Call(T.values))) == ['b'] with pytest.raises(TypeError, match='expected func to be a callable or T'): Call(func=object()) return
def test_t_dunders(): with raises(AttributeError) as exc_info: T.__name__ assert 'use T.__("name__")' in str(exc_info.value) assert glom(1, T.__('class__')) is int
def test_path_access_error_message(): # test fuzzy access with raises(GlomError) as exc_info: glom({}, 'a.b') assert ( "PathAccessError: could not access 'a', part 0 of Path('a', 'b'), got error: KeyError" in exc_info.exconly()) ke = repr(KeyError('a')) # py3.7+ changed the keyerror repr assert repr( exc_info.value) == "PathAccessError(" + ke + ", Path('a', 'b'), 0)" # test multi-part Path with T, catchable as a KeyError with raises(KeyError) as exc_info: # don't actually use glom to copy your data structures please glom({'a': {'b': 'c'}}, Path('a', T.copy(), 'd')) assert ( "PathAccessError: could not access 'd', part 3 of Path('a', T.copy(), 'd'), got error: KeyError" in exc_info.exconly()) ke = repr(KeyError('d')) # py3.7+ changed the keyerror repr assert repr(exc_info.value ) == "PathAccessError(" + ke + ", Path('a', T.copy(), 'd'), 3)" # test AttributeError with raises(GlomError) as exc_info: glom({'a': {'b': 'c'}}, Path('a', T.b)) assert ( "PathAccessError: could not access 'b', part 1 of Path('a', T.b), got error: AttributeError" in exc_info.exconly()) ae = repr(AttributeError("'dict' object has no attribute 'b'")) assert repr( exc_info.value) == "PathAccessError(" + ae + ", Path(\'a\', T.b), 1)"
def render_home(ptcs): ctx = glom(ptcs, {'campaigns': [T.get_summary_ctx()]}) index_html = ASHES_ENV.render('index.html', ctx) index_path = STATIC_PATH + '/index.html' with atomic_save(index_path) as f: f.write(index_html.encode('utf-8')) return
def _spec_to_type( key: str, value: Dict[str, Dict], bases: Tuple[Type, ...] = ()) -> Type: """Using the type specification, create the custom type objects Parameters ---------- key : str The key name corresponding to the specification. It is used as a template for the custom type name. value : Dict The dictionary with the type specification. It looks like: { "key1": {"type": <type1>, "validator": <validator1>}, "key2": {"type": <type2>, "validator": <validator2>}, # ... } bases : Tuple[Type] Base classes Returns ------- Type Custom type object with validators """ fields = glom( value.items(), [( { "k": "0", "v": "1.type", # TODO: non-trivial defaults like mutable types "d": Coalesce("1.default", default=SKIP), }, T.values(), tuple, )], ) # extract key, value and convert to list of tuples ns = dict( chain(*glom( value.values(), [(Coalesce("validator", default_factory=dict), T.items())], ))) # chain dict.items() and create namespace return make_dataconfig(f"{key}_t", fields, namespace=ns, bases=bases)
def test_path_len(): assert len(Path()) == 0 assert len(Path('a', 'b', 'c')) == 3 assert len(Path.from_text('1.2.3.4')) == 4 assert len(Path(T)) == 0 assert len(Path(T.a.b.c)) == 3 assert len(Path(T.a()['b'].c.d)) == 5
def test_path_items(): path = Path(T.a, 1, 2, T(test='yes')) assert path.items() == (('.', 'a'), ('P', 1), ('P', 2), ('(', ((), { 'test': 'yes' }))) assert Path().items() == ()
def test_df_schema_flags(csvfile, flags): df = csv_sample(csvfile) res = df_schema_flags(df, flags) assert glom(res, ["datatype"]) == ["yesno"] * len(res) # NOTE: index 0 relies on deterinistic dictionary order assert glom(res, [(tuple, "0")]) == df.columns.to_list() assert glom(res, [(T.values(), tuple, "0")]) == [ _FLAG_FMT.format(name=flags.instanceName, col=col) for col in df.columns ]
def handle(self, *args, person_email, **options): try: p = Person.objects.get_by_natural_key(person_email) except Person.DoesNotExist: raise CommandError("L'email donné est inconnu.") spec_role = get_all_fields(Role) spec_event = { "Nom": "name", "URL": ("id", lambda id: front_url("view_event", args=[id])), } spec_membership = { "Nom": "supportgroup.name", "URL": ("supportgroup.id", lambda id: front_url("view_group", args=[id])), "Animateur": "is_referent", "Gestionnaire": "is_manager", } spec_payment = get_all_fields(Payment) spec_subscription = get_all_fields(Subscription) spec_event_images = get_all_fields(EventImage) spec_form_submissions = get_all_fields(PersonFormSubmission) spec_tags = get_all_fields(PersonTag) spec_person = { **get_all_fields(Person), "pays": ("location_country", str), "Rôle": ("role", spec_role), "événements organisés": ("organized_events", T.all(), [spec_event]), "participations aux événements": ( "rsvps", T.all(), [("event", spec_event)], ), "participations à des groupes": ("memberships", T.all(), [spec_membership]), "paiements": ("payments", T.all(), [spec_payment]), "souscription au don mensuel": Coalesce(("subscription", spec_subscription), default=None), "images d'événements": ("event_images", T.all(), [spec_event_images]), "réponses à des formulaires": ( "form_submissions", T.all(), [spec_form_submissions], ), "libellés": ("tags", T.all(), [spec_tags]), } self.stdout.ending = "" json.dump(glom(p, spec_person), self.stdout, cls=DjangoJSONEncoder)
def test_df_schema_dtype(csvfile, flags): df = csv_sample(csvfile) flags, flagged_cols = set_flags(flags, df.columns) res = df_schema_dtype(df, flags) assert glom(res, ["choices"]) == [_TYPES] * len(res) # NOTE: index 0 relies on deterinistic dictionary order assert glom(res, [(tuple, "0")]) == flagged_cols assert glom(res, [(T.values(), tuple, "0")]) == [ _FLAG_FMT.format(name=flags.instanceName, col=col) for col in flagged_cols ]
def serialize_notifications(notifications): # All fields are either spec = [{ "id": "id", "status": "status", "content": Coalesce(T.html_content(), T.announcement.html_content(), skip="", default=""), "icon": Coalesce("icon", "announcement.icon", skip="", default=""), "link": Coalesce("link", "announcement.link", skip="", default=""), "created": (Coalesce("announcement.start_date", "created"), T.isoformat()), }] return glom(notifications, spec)
def get_room_access_codes(url=BBB_ACCESS_LIST_URL): r = requests.get(url) if r.status_code == 200: spec = ('rooms', [T.values()], [tuple], [{ T[0].upper(): { 'url': T[1], 'access_code': T[2] } }], merge) return glom(r.json(), spec) else: return {}
def get_proc_info(): ret = {} ret['pid'] = os.getpid() _user_t, _sys_t = os.times()[:2] ret['cpu_times'] = {'user_time': _user_t, 'sys_time': _sys_t} ret['cwd'] = os.getcwdu() ret['umask'] = os.umask(os.umask(2)) # have to set to get ret['umask_str'] = '{0:03o}'.format(ret['umask']) ret['owner'] = glom(globals(), Coalesce(T['getpass'].getuser(), T['os'].getuid()), skip_exc=Exception) # use 0 to get current niceness, seems to return process group's nice level unix_only_vals = glom(os, {'ppid': T.getppid(), 'pgid': T.getpgrp(), 'niceness': T.nice(0)}, skip_exc=AttributeError) ret.update(unix_only_vals) ret['rusage'] = get_rusage_dict() ret['rlimit'] = get_rlimit_dict() return ret
def admin_summary(spending_request): spec = { "id": "id", "title": "title", "status": T.get_status_display(), "group": ("group", group_formatter), "event": "event", "category": T.get_category_display(), "category_precisions": "category_precisions", "explanation": "explanation", "amount": ("amount", display_price), "spending_date": "spending_date", "provider": "provider", "iban": "iban", "payer_name": "payer_name", } values = glom(spending_request, spec) return [{ "label": get_spending_request_field_label(f), "value": values[f] } for f in spec]
def test_path_slices(): path = Path(T.a.b, 1, 2, T(test='yes')) assert path[::] == path # positive indices assert path[3:] == Path(2, T(test='yes')) assert path[1:3] == Path(T.b, 1) assert path[:3] == Path(T.a.b, 1) # positive indices backwards assert path[2:1] == Path() # negative indices forward assert path[-1:] == Path(T(test='yes')) assert path[:-2] == Path(T.a.b, 1) assert path[-3:-1] == Path(1, 2) # negative indices backwards assert path[-1:-3] == Path() # slicing and stepping assert path[1::2] == Path(T.b, 2)
def get_host_info(): ret = {} now = datetime.datetime.utcnow() ret['hostname'] = socket.gethostname() ret['hostfqdn'] = socket.getfqdn() ret['uname'] = platform.uname() ret['cpu_count'] = CPU_COUNT ret['platform'] = platform.platform() ret['platform_terse'] = platform.platform(terse=True) ret['load_avgs'] = glom(os, T.getloadavg(), skip_exc=AttributeError) ret['utc_time'] = str(now) return ret
def get_pyvm_info(): ret = {} ret['executable'] = sys.executable ret['is_64bit'] = IS_64BIT ret['version'] = sys.version ret['compiler'] = platform.python_compiler() ret['build_date'] = platform.python_build()[1] ret['version_info'] = list(sys.version_info) ret['have_ucs4'] = getattr(sys, 'maxunicode', 0) > 65536 ret['have_readline'] = HAVE_READLINE ret['active_thread_count'] = glom(sys, T._current_frames().__len__(), skip_exc=Exception) ret['recursion_limit'] = sys.getrecursionlimit() ret['gc'] = glom(None, Call(get_gc_info), skip_exc=Exception) # effectively try/except:pass ret['check_interval'] = sys.getcheckinterval() return ret
def test_while(): cnt = count() out = glom(cnt, Iter().takewhile(lambda x: x < 3)) assert list(out) == [0, 1, 2] assert next(cnt) == 4 assert repr(Iter().takewhile(T.a) == 'Iter().takewhile(T.a)') range_iter = iter(range(7)) out = glom(range_iter, Iter().dropwhile(lambda x: x < 3 or x > 5)) assert list(out) == [3, 4, 5, 6] # 6 still here despite the x>5 above out = glom(range(10), Iter().dropwhile(lambda x: x >= 0).limit(10)) assert list(out) == [] out = glom(range(8), Iter().dropwhile((T.bit_length(), lambda x: x < 3))) assert list(out) == [4, 5, 6, 7] assert repr(Iter().dropwhile(T.a) == 'Iter().dropwhile(T.a)')
def test_path_t_roundtrip(): # check that T repr roundrips assert repr(T['a'].b.c()) == "T['a'].b.c()" # check that Path repr roundtrips assert repr(Path('a', 1, 'b.b', -1.0)) == "Path('a', 1, 'b.b', -1.0)" # check that Path repr roundtrips when it contains Ts assert repr(Path(T['a'].b, 'c', T['d'].e)) == "Path(T['a'].b, 'c', T['d'].e)" # check that T instances containing Path access revert to repring with Path assert repr(Path(T['a'].b, 'c', T['d'].e).path_t) == "Path(T['a'].b, 'c', T['d'].e)" # check that Paths containing only T objects reduce to a T (joining the T objects) assert repr(Path(T['a'].b, T.c())) == "T['a'].b.c()" # check that multiple nested paths reduce assert repr(Path(Path(Path('a')))) == "Path('a')"
def test_call_and_target(): class F(object): def __init__(s, a, b, c): s.a, s.b, s.c = a, b, c val = glom(1, Call(F, kwargs=dict(a=T, b=T, c=T))) assert (val.a, val.b, val.c) == (1, 1, 1) class F(object): def __init__(s, a): s.a = a val = glom({'one': F('two')}, Call(F, args=(T['one'].a, ))) assert val.a == 'two' assert glom({'a': 1}, Call(F, kwargs=T)).a == 1 assert glom([1], Call(F, args=T)).a == 1 assert glom(F, T(T)).a == F assert glom([F, 1], T[0](T[1]).a) == 1 assert glom([[1]], T[0][0][0][UP]) == 1
def get_pyvm_info(): ret = {} ret['executable'] = sys.executable ret['is_64bit'] = IS_64BIT ret['version'] = sys.version ret['compiler'] = platform.python_compiler() ret['build_date'] = platform.python_build()[1] ret['version_info'] = list(sys.version_info) ret['have_ucs4'] = getattr(sys, 'maxunicode', 0) > 65536 ret['have_readline'] = HAVE_READLINE ret['active_thread_count'] = glom(sys, (T._current_frames(), len), skip_exc=Exception) ret['recursion_limit'] = sys.getrecursionlimit() ret['gc'] = glom(None, Call(get_gc_info), skip_exc=Exception) # effectively try/except:pass get_interval = getattr(sys, 'getswitchinterval', sys.getcheckinterval) ret['check_interval'] = get_interval() return ret
def report_start(self, meta: GitMeta) -> None: """ Get scan id and file ignores returns name of policy used to scan """ debug_echo(f"=== reporting start to semgrep app at {self.url}") response = self.session.post( f"{self.url}/api/agent/deployment/{self.deployment_id}/scan", json={"meta": meta.to_dict()}, timeout=30, ) debug_echo(f"=== POST .../scan responded: {response!r}") if response.status_code == 404: raise ActionFailure( "Failed to create a scan with given token and deployment_id." "Please make sure they have been set correctly." f"API server at {self.url} returned this response: {response.text}" ) try: response.raise_for_status() except requests.RequestException: raise ActionFailure( f"API server at {self.url} returned this error: {response.text}" ) else: body = response.json() self.scan = Scan( id=glom(body, T["scan"]["id"]), ignore_patterns=glom( body, T["scan"]["meta"].get("ignored_files", [])), policy_list=glom(body, T["policy"]), autofix=glom(body, T.get("autofix", False)), ) debug_echo(f"=== Our scan object is: {self.scan!r}")
def test_path_access_error_message(): # test fuzzy access with raises(GlomError) as exc_info: glom({}, 'a.b') assert ("PathAccessError: could not access 'a', part 0 of Path('a', 'b'), got error: KeyError" in exc_info.exconly()) assert repr(exc_info.value) == "PathAccessError(KeyError('a',), Path('a', 'b'), 0)" # test multi-part Path with T, catchable as a KeyError with raises(KeyError) as exc_info: # don't actually use glom to copy your data structures please glom({'a': {'b': 'c'}}, Path('a', T.copy(), 'd')) assert ("PathAccessError: could not access 'd', part 3 of Path('a', T.copy(), 'd'), got error: KeyError" in exc_info.exconly()) assert repr(exc_info.value) == "PathAccessError(KeyError('d',), Path('a', T.copy(), 'd'), 3)" # test AttributeError with raises(GlomError) as exc_info: glom({'a': {'b': 'c'}}, Path('a', T.b)) assert ("PathAccessError: could not access 'b', part 1 of Path('a', T.b), got error: AttributeError" in exc_info.exconly()) assert repr(exc_info.value) == """PathAccessError(AttributeError("\'dict\' object has no attribute \'b\'",), Path(\'a\', T.b), 1)"""
logger = logging.getLogger(__name__) def date_locale(d): return d.astimezone(timezone.get_current_timezone()) PAYMENT_SPEC = { "créé": ("created", date_locale), "dernier événement": ("modified", date_locale), "id": "id", "email": "email", "nom": "last_name", "prenom": "first_name", "telephone": Coalesce("phone_number.as_international", default=None), "statut": T.get_status_display(), "montant": ("price", lambda m: Decimal(m) / 100), "type": "type", "mode": "mode", "abonnement associé": "subscription_id", "adresse1": Coalesce(T.meta["location_address1"], default=None), "adresse2": Coalesce(T.meta["location_address1"], default=None), "code postal": Coalesce(T.meta["location_zip"], default=None), "ville": Coalesce(T.meta["location_city"], default=None), "pays": Coalesce(T.meta["location_country"], default=None), } STATUS_MAPPING = { f[len("STATUS_") :]: getattr(Payment, f) for f in dir(Payment)
def test_check_basic(): assert glom([0, SKIP], [T]) == [0] # sanity check SKIP target = [{'id': 0}, {'id': 1}, {'id': 2}] # check that skipping non-passing values works assert glom(target, ([Coalesce(Check('id', equal_to=0), default=SKIP)], T[0])) == {'id': 0} assert glom(target, ([Check('id', equal_to=0, default=SKIP)], T[0])) == {'id': 0} # check that stopping iteration on non-passing values works assert glom(target, [Check('id', equal_to=0, default=STOP)]) == [{'id': 0}] # check that stopping chain execution on non-passing values works spec = (Check(validate=lambda x: len(x) > 0, default=STOP), T[0]) assert glom('hello', spec) == 'h' assert glom('', spec) == '' # would fail with IndexError if STOP didn't work assert repr(Check()) == 'Check()' assert repr(Check(T.a)) == 'Check(T.a)' assert repr(Check(equal_to=1)) == 'Check(equal_to=1)' assert repr(Check(instance_of=dict)) == 'Check(instance_of=dict)' assert repr(Check(T(len), validate=sum)) == 'Check(T(len), validate=sum)' target = [1, u'a'] assert glom(target, [Check(type=unicode, default=SKIP)]) == ['a'] assert glom(target, [Check(type=(unicode, int))]) == [1, 'a'] assert glom(target, [Check(instance_of=unicode, default=SKIP)]) == ['a'] assert glom(target, [Check(instance_of=(unicode, int))]) == [1, 'a'] target = ['1'] assert glom(target, [Check(validate=(int, float))]) assert glom(target, [Check()]) # bare check does a truthy check failing_checks = [ ({'a': {'b': 1}}, {'a': ('a', 'b', Check(type=str))}, '''target at path ['a', 'b'] failed check, got error: "expected type to be 'str', found type 'int'"'''), ({'a': {'b': 1}}, {'a': ('a', Check('b', type=str))}, '''target at path ['a'] failed check, subtarget at 'b' got ''' '''error: "expected type to be 'str', found type 'int'"'''), (1, Check(type=(unicode, bool))), (1, Check(instance_of=unicode)), (1, Check(instance_of=(unicode, bool))), (1, Check(equal_to=0)), (1, Check(one_of=(0,))), (1, Check(one_of=(0, 2))), ('-3.14', Check(validate=int)), ('', Check(validate=lambda x: False))] for fc in failing_checks: if len(fc) == 2: target, check = fc msg = None else: target, check, msg = fc with raises(CheckError) as exc_info: glom(target, check) if msg is not None: assert str(exc_info.value).find(msg) != -1 assert repr(exc_info.value)
from io import BytesIO import pandas as pd from django.core.mail import EmailMessage, get_connection from django.core.management.base import BaseCommand from django.utils import timezone from glom import Coalesce, T, glom from agir.lib.management_utils import month_argument, month_range, email_argument from agir.payments.models import Payment from agir.system_pay.models import SystemPayTransaction FILE_DESC = { "Code_uuid": ( "systempaytransaction_set", T.get(status=SystemPayTransaction.STATUS_COMPLETED), "uuid", T.hex, ), "No_abonnement": "subscription.id", "Email": Coalesce("person.email", "email"), "Nom": Coalesce("person.last_name", "subscription.meta.last_name"), "Prénom": Coalesce( "person.first_name", "subscription.meta.first_name", skip=("",), default="" ), "No_et_Voie": Coalesce( "person.location_address1", "subscription.meta.location_address1", skip=("",), default="", ),
def test_path_values(): path = Path(T.a.b, 1, 2, T(test='yes')) assert path.values() == ('a', 'b', 1, 2, ((), {'test': 'yes'})) assert Path().values() == ()
def test_corner_cases(): target = range(5) # immediate stop dict assert glom(target, Group({(lambda t: STOP): [T]})) == {} # immediate stop list assert glom(target, Group([lambda t: STOP])) == [] # dict key SKIP assert glom(target, Group({(lambda t: SKIP if t < 3 else t): T})) == { 3: 3, 4: 4 } # dict val SKIP assert glom(target, Group({T: lambda t: t if t % 2 else SKIP})) == { 3: 3, 1: 1 } # list val SKIP assert glom(target, Group([lambda t: t if t % 2 else SKIP])) == [1, 3] # embedded auto spec (lol @ 0 being 0 bit length) assert glom(target, Group({Auto(('bit_length', T())): [T]})) == { 0: [0], 1: [1], 2: [2, 3], 3: [4] } # no dicts inside lists in Group mode with raises(BadSpec): assert glom(target, Group([{T: T}])) # check only supported types with raises(BadSpec): assert glom(target, Group('no string support yet')) # bucket ints by their bit length and then odd/even, limited to 3 per bucket spec = Group({T.bit_length(): {lambda t: t % 2: Limit(3)}}) res = glom(range(20), spec) assert res == { 0: { 0: [0] }, 1: { 1: [1] }, 2: { 0: [2], 1: [3] }, 3: { 0: [4, 6], 1: [5, 7] }, 4: { 0: [8, 10, 12], 1: [9, 11, 13] } } return