{"id":42127353,"url":"https://github.com/sileod/xpflow","last_synced_at":"2026-01-26T15:11:45.977Z","repository":{"id":57477944,"uuid":"401655268","full_name":"sileod/xpflow","owner":"sileod","description":"Python nested loops as classes for improved readability and modularity","archived":false,"fork":false,"pushed_at":"2024-01-03T14:47:38.000Z","size":87,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-29T01:26:19.303Z","etag":null,"topics":["classes","context-manager","experiment","experiments-tracking","for-loop","hydra","loops","nested-loops","python","pythonic","wandb"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sileod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-08-31T10:01:46.000Z","updated_at":"2025-09-12T12:37:40.000Z","dependencies_parsed_at":"2024-01-03T15:48:25.817Z","dependency_job_id":"73f7c0cd-2da3-41fc-8e22-2ce42b9af63c","html_url":"https://github.com/sileod/xpflow","commit_stats":{"total_commits":94,"total_committers":2,"mean_commits":47.0,"dds":"0.010638297872340385","last_synced_commit":"305246fc103f41b364b1b0ae968001e0b382a816"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/sileod/xpflow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sileod%2Fxpflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sileod%2Fxpflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sileod%2Fxpflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sileod%2Fxpflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sileod","download_url":"https://codeload.github.com/sileod/xpflow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sileod%2Fxpflow/sbom","scorecard":{"id":823677,"data":{"date":"2025-08-11","repo":{"name":"github.com/sileod/xpflow","commit":"962fefb3c3facda9117e406b6717fceb8ebd4296"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/pipy_release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pipy_release.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/sileod/xpflow/pipy_release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pipy_release.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/sileod/xpflow/pipy_release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/pipy_release.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/sileod/xpflow/pipy_release.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/pipy_release.yml:20","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   1 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-23T16:11:36.016Z","repository_id":57477944,"created_at":"2025-08-23T16:11:36.016Z","updated_at":"2025-08-23T16:11:36.016Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28781308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T13:55:28.044Z","status":"ssl_error","status_checked_at":"2026-01-26T13:55:26.068Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["classes","context-manager","experiment","experiments-tracking","for-loop","hydra","loops","nested-loops","python","pythonic","wandb"],"created_at":"2026-01-26T15:11:45.921Z","updated_at":"2026-01-26T15:11:45.969Z","avatar_url":"https://github.com/sileod.png","language":"Python","readme":"# xpflow: nested loops as structures (class/dict)\n\nDid you ever perform experiments by nesting loops like this ? \n```python\nargs = edict({'a':'A'})\n\nfor b in [1,2]:\n    for lr in [1e-3, 2e-3]:\n        args.lr = lr\n        args.b = b\n        # perform_experiment_and_logging(args)\n```\n\nThis get messy when there are many loops. In addition, nested loops are not objects, so you cannot store them or share them. A better alternative is to represent experiments with dictionaries where some values are lists, e.g.: \n```python \nlearning_rate = {\n    'a' : 'A',\n    'b' : [1, 2],\n    'lr' : [1e-3, 2e-3]\n}\n```\nHowever, you have to write custom code to take care of the list values.\n\n`xpflow` does that under the hood. Lists of values are used to denote multiple values to try for a given field. All combinations will be generated in the form of EasyDict objects. Nested loops become objects.\n\n```python\nfrom xpflow import Xp\n\nfor args in Xp(learning_rate):\n    # perform_experiment_and_logging(args)\n```\nThis allows a readable, shareable, composable, and framework-agnostic formulation of experiments. You can also use classes instead of dictionaries, they are more composable and look cleaner.\n\n```python\n\nclass learning_rate(Xp):\n    a = 'A'\n    b = [1, 2]\n    lr = [1e-3, 2e-3]\n    \nfor args in learning_rate():\n    # perform_experiment_and_logging(args)\n```\n\n## Installation and usage\n```\npip install xpflow\n```\n\nJust make sure that your experiment classes inherits the Xp class. Instanciating the class will provide an iterable yielding the possible value combinations.\nLists of values will be used to generate the possible combinations. You can use a list of lists to represent values that should actually be lists.\n\n```python\nfrom xpflow import Xp\n\nclass base(Xp):\n    a='A'\n    b=[1,2]\n\nclass learning_rate(base):\n    lr = [1e-3, 2e-3]\n    list_values = [[5, 6]]\n    \nfor args in learning_rate():\n    # perform_experiment_and_logging(args)\n    print(args.a, args.b, args.lr, args.list_values)\n```\nwill print the following output:\n```\nA 1 0.001 [5, 6]\nA 1 0.002 [5, 6]\nA 2 0.001 [5, 6]\nA 2 0.002 [5, 6]\n```\n\n##  Other specific use cases:\n\n#### Sequential experiments\n```python\nfor args in xp1() + xp2()\n    # perform_experiment_and_logging(args)\n\n```\n\n### Command line arguments\n\nxpflows automatically manages command line arguments if you want to use your code in a script instead of interactive mode. Just pass arguments, and if they are in a Xp class, they will be overriden with a type matching the original value.\n\n#### Distributing computations across processes\nYou can easily distribute the computations across processes by passing argparse arguments to your main script. \nThe argument yielded by `xpflow` are *deterministically* hashable into integers (standard dict/edict are not hashable).\n\n```python\nfor args in xp():\n    if hash(args) % argparse_args.number_of_processes != argparse_args.process_index:\n        continue\n    # perform_experiment_and_logging(args)\n```\n\n#### Random search\n\nYou can perform a random search by using lengthy lists of possible values and then randomly discarding parameter combinations.\n\n```python\nclass random_search_space(Xp):\n    learning_rate=list(np.logspace(-6,-1,100))\n    batch_size=[32,64,128,256]\n    nb_epochs=[3,4,5]\n\nfor args in sorted(random_search_space(), key=hash)[:100]:\n    # perform_experiment_and_logging(args)\n```\n\n#### Context managers\n\nxpflow also provides context managers to faciliate sequential experiments by catching exceptions more concisely than using `try/except`\n```python\nwith xpflow.Catch():\n    #do_stuff\nprint(_EXCEPTIONS)\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsileod%2Fxpflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsileod%2Fxpflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsileod%2Fxpflow/lists"}