From a9c62055696e147f4e412a616e0975cad82fcc67 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 08:50:04 -0500 Subject: [PATCH 01/27] Renamed mepo.d -> src/mepo --- {mepo.d => src/mepo}/cmdline/branch_parser.py | 0 {mepo.d => src/mepo}/cmdline/config_parser.py | 0 {mepo.d => src/mepo}/cmdline/parser.py | 0 {mepo.d => src/mepo}/cmdline/stash_parser.py | 0 {mepo.d => src/mepo}/cmdline/tag_parser.py | 0 {mepo.d => src/mepo}/command/branch/branch.py | 0 {mepo.d => src/mepo}/command/branch/create/create.py | 0 {mepo.d => src/mepo}/command/branch/delete/delete.py | 0 {mepo.d => src/mepo}/command/branch/list/list.py | 0 {mepo.d => src/mepo}/command/changed-files/changed-files.py | 0 .../mepo}/command/checkout-if-exists/checkout-if-exists.py | 0 {mepo.d => src/mepo}/command/checkout/checkout.py | 0 {mepo.d => src/mepo}/command/clone/clone.py | 0 {mepo.d => src/mepo}/command/command.py | 0 {mepo.d => src/mepo}/command/commit/commit.py | 0 {mepo.d => src/mepo}/command/compare/compare.py | 0 {mepo.d => src/mepo}/command/config/config.py | 0 {mepo.d => src/mepo}/command/config/delete/delete.py | 0 {mepo.d => src/mepo}/command/config/get/get.py | 0 {mepo.d => src/mepo}/command/config/print/print.py | 0 {mepo.d => src/mepo}/command/config/set/set.py | 0 {mepo.d => src/mepo}/command/develop/develop.py | 0 {mepo.d => src/mepo}/command/diff/diff.py | 0 {mepo.d => src/mepo}/command/fetch/fetch.py | 0 {mepo.d => src/mepo}/command/init/init.py | 0 {mepo.d => src/mepo}/command/list/list.py | 0 {mepo.d => src/mepo}/command/pull-all/pull-all.py | 0 {mepo.d => src/mepo}/command/pull/pull.py | 0 {mepo.d => src/mepo}/command/push/push.py | 0 {mepo.d => src/mepo}/command/reset/reset.py | 0 {mepo.d => src/mepo}/command/restore-state/restore-state.py | 0 {mepo.d => src/mepo}/command/save/save.py | 0 {mepo.d => src/mepo}/command/stage/stage.py | 0 {mepo.d => src/mepo}/command/stash/apply/apply.py | 0 {mepo.d => src/mepo}/command/stash/list/list.py | 0 {mepo.d => src/mepo}/command/stash/pop/pop.py | 0 {mepo.d => src/mepo}/command/stash/push/push.py | 0 {mepo.d => src/mepo}/command/stash/show/show.py | 0 {mepo.d => src/mepo}/command/stash/stash.py | 0 {mepo.d => src/mepo}/command/status/status.py | 0 {mepo.d => src/mepo}/command/tag/create/create.py | 0 {mepo.d => src/mepo}/command/tag/delete/delete.py | 0 {mepo.d => src/mepo}/command/tag/list/list.py | 0 {mepo.d => src/mepo}/command/tag/push/push.py | 0 {mepo.d => src/mepo}/command/tag/tag.py | 0 {mepo.d => src/mepo}/command/unstage/unstage.py | 0 {mepo.d => src/mepo}/command/whereis/whereis.py | 0 {mepo.d => src/mepo}/config/config_file.py | 0 {mepo.d => src/mepo}/main.py | 0 {mepo.d => src/mepo}/repository/git.py | 0 {mepo.d => src/mepo}/state/component.py | 0 {mepo.d => src/mepo}/state/exceptions.py | 0 {mepo.d => src/mepo}/state/state.py | 0 {mepo.d => src/mepo}/utest/input/args.py | 0 {mepo.d => src/mepo}/utest/input/components.yaml | 0 {mepo.d => src/mepo}/utest/output/compare_brief_output.txt | 0 {mepo.d => src/mepo}/utest/output/compare_full_output.txt | 0 {mepo.d => src/mepo}/utest/output/list_output.txt | 0 {mepo.d => src/mepo}/utest/output/status_output.txt | 0 {mepo.d => src/mepo}/utest/test_mepo_commands.py | 0 {mepo.d => src/mepo}/utilities/colors.py | 0 {mepo.d => src/mepo}/utilities/mepoconfig.py | 0 {mepo.d => src/mepo}/utilities/shellcmd.py | 0 {mepo.d => src/mepo}/utilities/verify.py | 0 {mepo.d => src/mepo}/utilities/version.py | 0 65 files changed, 0 insertions(+), 0 deletions(-) rename {mepo.d => src/mepo}/cmdline/branch_parser.py (100%) rename {mepo.d => src/mepo}/cmdline/config_parser.py (100%) rename {mepo.d => src/mepo}/cmdline/parser.py (100%) rename {mepo.d => src/mepo}/cmdline/stash_parser.py (100%) rename {mepo.d => src/mepo}/cmdline/tag_parser.py (100%) rename {mepo.d => src/mepo}/command/branch/branch.py (100%) rename {mepo.d => src/mepo}/command/branch/create/create.py (100%) rename {mepo.d => src/mepo}/command/branch/delete/delete.py (100%) rename {mepo.d => src/mepo}/command/branch/list/list.py (100%) rename {mepo.d => src/mepo}/command/changed-files/changed-files.py (100%) rename {mepo.d => src/mepo}/command/checkout-if-exists/checkout-if-exists.py (100%) rename {mepo.d => src/mepo}/command/checkout/checkout.py (100%) rename {mepo.d => src/mepo}/command/clone/clone.py (100%) rename {mepo.d => src/mepo}/command/command.py (100%) rename {mepo.d => src/mepo}/command/commit/commit.py (100%) rename {mepo.d => src/mepo}/command/compare/compare.py (100%) rename {mepo.d => src/mepo}/command/config/config.py (100%) rename {mepo.d => src/mepo}/command/config/delete/delete.py (100%) rename {mepo.d => src/mepo}/command/config/get/get.py (100%) rename {mepo.d => src/mepo}/command/config/print/print.py (100%) rename {mepo.d => src/mepo}/command/config/set/set.py (100%) rename {mepo.d => src/mepo}/command/develop/develop.py (100%) rename {mepo.d => src/mepo}/command/diff/diff.py (100%) rename {mepo.d => src/mepo}/command/fetch/fetch.py (100%) rename {mepo.d => src/mepo}/command/init/init.py (100%) rename {mepo.d => src/mepo}/command/list/list.py (100%) rename {mepo.d => src/mepo}/command/pull-all/pull-all.py (100%) rename {mepo.d => src/mepo}/command/pull/pull.py (100%) rename {mepo.d => src/mepo}/command/push/push.py (100%) rename {mepo.d => src/mepo}/command/reset/reset.py (100%) rename {mepo.d => src/mepo}/command/restore-state/restore-state.py (100%) rename {mepo.d => src/mepo}/command/save/save.py (100%) rename {mepo.d => src/mepo}/command/stage/stage.py (100%) rename {mepo.d => src/mepo}/command/stash/apply/apply.py (100%) rename {mepo.d => src/mepo}/command/stash/list/list.py (100%) rename {mepo.d => src/mepo}/command/stash/pop/pop.py (100%) rename {mepo.d => src/mepo}/command/stash/push/push.py (100%) rename {mepo.d => src/mepo}/command/stash/show/show.py (100%) rename {mepo.d => src/mepo}/command/stash/stash.py (100%) rename {mepo.d => src/mepo}/command/status/status.py (100%) rename {mepo.d => src/mepo}/command/tag/create/create.py (100%) rename {mepo.d => src/mepo}/command/tag/delete/delete.py (100%) rename {mepo.d => src/mepo}/command/tag/list/list.py (100%) rename {mepo.d => src/mepo}/command/tag/push/push.py (100%) rename {mepo.d => src/mepo}/command/tag/tag.py (100%) rename {mepo.d => src/mepo}/command/unstage/unstage.py (100%) rename {mepo.d => src/mepo}/command/whereis/whereis.py (100%) rename {mepo.d => src/mepo}/config/config_file.py (100%) rename {mepo.d => src/mepo}/main.py (100%) rename {mepo.d => src/mepo}/repository/git.py (100%) rename {mepo.d => src/mepo}/state/component.py (100%) rename {mepo.d => src/mepo}/state/exceptions.py (100%) rename {mepo.d => src/mepo}/state/state.py (100%) rename {mepo.d => src/mepo}/utest/input/args.py (100%) rename {mepo.d => src/mepo}/utest/input/components.yaml (100%) rename {mepo.d => src/mepo}/utest/output/compare_brief_output.txt (100%) rename {mepo.d => src/mepo}/utest/output/compare_full_output.txt (100%) rename {mepo.d => src/mepo}/utest/output/list_output.txt (100%) rename {mepo.d => src/mepo}/utest/output/status_output.txt (100%) rename {mepo.d => src/mepo}/utest/test_mepo_commands.py (100%) rename {mepo.d => src/mepo}/utilities/colors.py (100%) rename {mepo.d => src/mepo}/utilities/mepoconfig.py (100%) rename {mepo.d => src/mepo}/utilities/shellcmd.py (100%) rename {mepo.d => src/mepo}/utilities/verify.py (100%) rename {mepo.d => src/mepo}/utilities/version.py (100%) diff --git a/mepo.d/cmdline/branch_parser.py b/src/mepo/cmdline/branch_parser.py similarity index 100% rename from mepo.d/cmdline/branch_parser.py rename to src/mepo/cmdline/branch_parser.py diff --git a/mepo.d/cmdline/config_parser.py b/src/mepo/cmdline/config_parser.py similarity index 100% rename from mepo.d/cmdline/config_parser.py rename to src/mepo/cmdline/config_parser.py diff --git a/mepo.d/cmdline/parser.py b/src/mepo/cmdline/parser.py similarity index 100% rename from mepo.d/cmdline/parser.py rename to src/mepo/cmdline/parser.py diff --git a/mepo.d/cmdline/stash_parser.py b/src/mepo/cmdline/stash_parser.py similarity index 100% rename from mepo.d/cmdline/stash_parser.py rename to src/mepo/cmdline/stash_parser.py diff --git a/mepo.d/cmdline/tag_parser.py b/src/mepo/cmdline/tag_parser.py similarity index 100% rename from mepo.d/cmdline/tag_parser.py rename to src/mepo/cmdline/tag_parser.py diff --git a/mepo.d/command/branch/branch.py b/src/mepo/command/branch/branch.py similarity index 100% rename from mepo.d/command/branch/branch.py rename to src/mepo/command/branch/branch.py diff --git a/mepo.d/command/branch/create/create.py b/src/mepo/command/branch/create/create.py similarity index 100% rename from mepo.d/command/branch/create/create.py rename to src/mepo/command/branch/create/create.py diff --git a/mepo.d/command/branch/delete/delete.py b/src/mepo/command/branch/delete/delete.py similarity index 100% rename from mepo.d/command/branch/delete/delete.py rename to src/mepo/command/branch/delete/delete.py diff --git a/mepo.d/command/branch/list/list.py b/src/mepo/command/branch/list/list.py similarity index 100% rename from mepo.d/command/branch/list/list.py rename to src/mepo/command/branch/list/list.py diff --git a/mepo.d/command/changed-files/changed-files.py b/src/mepo/command/changed-files/changed-files.py similarity index 100% rename from mepo.d/command/changed-files/changed-files.py rename to src/mepo/command/changed-files/changed-files.py diff --git a/mepo.d/command/checkout-if-exists/checkout-if-exists.py b/src/mepo/command/checkout-if-exists/checkout-if-exists.py similarity index 100% rename from mepo.d/command/checkout-if-exists/checkout-if-exists.py rename to src/mepo/command/checkout-if-exists/checkout-if-exists.py diff --git a/mepo.d/command/checkout/checkout.py b/src/mepo/command/checkout/checkout.py similarity index 100% rename from mepo.d/command/checkout/checkout.py rename to src/mepo/command/checkout/checkout.py diff --git a/mepo.d/command/clone/clone.py b/src/mepo/command/clone/clone.py similarity index 100% rename from mepo.d/command/clone/clone.py rename to src/mepo/command/clone/clone.py diff --git a/mepo.d/command/command.py b/src/mepo/command/command.py similarity index 100% rename from mepo.d/command/command.py rename to src/mepo/command/command.py diff --git a/mepo.d/command/commit/commit.py b/src/mepo/command/commit/commit.py similarity index 100% rename from mepo.d/command/commit/commit.py rename to src/mepo/command/commit/commit.py diff --git a/mepo.d/command/compare/compare.py b/src/mepo/command/compare/compare.py similarity index 100% rename from mepo.d/command/compare/compare.py rename to src/mepo/command/compare/compare.py diff --git a/mepo.d/command/config/config.py b/src/mepo/command/config/config.py similarity index 100% rename from mepo.d/command/config/config.py rename to src/mepo/command/config/config.py diff --git a/mepo.d/command/config/delete/delete.py b/src/mepo/command/config/delete/delete.py similarity index 100% rename from mepo.d/command/config/delete/delete.py rename to src/mepo/command/config/delete/delete.py diff --git a/mepo.d/command/config/get/get.py b/src/mepo/command/config/get/get.py similarity index 100% rename from mepo.d/command/config/get/get.py rename to src/mepo/command/config/get/get.py diff --git a/mepo.d/command/config/print/print.py b/src/mepo/command/config/print/print.py similarity index 100% rename from mepo.d/command/config/print/print.py rename to src/mepo/command/config/print/print.py diff --git a/mepo.d/command/config/set/set.py b/src/mepo/command/config/set/set.py similarity index 100% rename from mepo.d/command/config/set/set.py rename to src/mepo/command/config/set/set.py diff --git a/mepo.d/command/develop/develop.py b/src/mepo/command/develop/develop.py similarity index 100% rename from mepo.d/command/develop/develop.py rename to src/mepo/command/develop/develop.py diff --git a/mepo.d/command/diff/diff.py b/src/mepo/command/diff/diff.py similarity index 100% rename from mepo.d/command/diff/diff.py rename to src/mepo/command/diff/diff.py diff --git a/mepo.d/command/fetch/fetch.py b/src/mepo/command/fetch/fetch.py similarity index 100% rename from mepo.d/command/fetch/fetch.py rename to src/mepo/command/fetch/fetch.py diff --git a/mepo.d/command/init/init.py b/src/mepo/command/init/init.py similarity index 100% rename from mepo.d/command/init/init.py rename to src/mepo/command/init/init.py diff --git a/mepo.d/command/list/list.py b/src/mepo/command/list/list.py similarity index 100% rename from mepo.d/command/list/list.py rename to src/mepo/command/list/list.py diff --git a/mepo.d/command/pull-all/pull-all.py b/src/mepo/command/pull-all/pull-all.py similarity index 100% rename from mepo.d/command/pull-all/pull-all.py rename to src/mepo/command/pull-all/pull-all.py diff --git a/mepo.d/command/pull/pull.py b/src/mepo/command/pull/pull.py similarity index 100% rename from mepo.d/command/pull/pull.py rename to src/mepo/command/pull/pull.py diff --git a/mepo.d/command/push/push.py b/src/mepo/command/push/push.py similarity index 100% rename from mepo.d/command/push/push.py rename to src/mepo/command/push/push.py diff --git a/mepo.d/command/reset/reset.py b/src/mepo/command/reset/reset.py similarity index 100% rename from mepo.d/command/reset/reset.py rename to src/mepo/command/reset/reset.py diff --git a/mepo.d/command/restore-state/restore-state.py b/src/mepo/command/restore-state/restore-state.py similarity index 100% rename from mepo.d/command/restore-state/restore-state.py rename to src/mepo/command/restore-state/restore-state.py diff --git a/mepo.d/command/save/save.py b/src/mepo/command/save/save.py similarity index 100% rename from mepo.d/command/save/save.py rename to src/mepo/command/save/save.py diff --git a/mepo.d/command/stage/stage.py b/src/mepo/command/stage/stage.py similarity index 100% rename from mepo.d/command/stage/stage.py rename to src/mepo/command/stage/stage.py diff --git a/mepo.d/command/stash/apply/apply.py b/src/mepo/command/stash/apply/apply.py similarity index 100% rename from mepo.d/command/stash/apply/apply.py rename to src/mepo/command/stash/apply/apply.py diff --git a/mepo.d/command/stash/list/list.py b/src/mepo/command/stash/list/list.py similarity index 100% rename from mepo.d/command/stash/list/list.py rename to src/mepo/command/stash/list/list.py diff --git a/mepo.d/command/stash/pop/pop.py b/src/mepo/command/stash/pop/pop.py similarity index 100% rename from mepo.d/command/stash/pop/pop.py rename to src/mepo/command/stash/pop/pop.py diff --git a/mepo.d/command/stash/push/push.py b/src/mepo/command/stash/push/push.py similarity index 100% rename from mepo.d/command/stash/push/push.py rename to src/mepo/command/stash/push/push.py diff --git a/mepo.d/command/stash/show/show.py b/src/mepo/command/stash/show/show.py similarity index 100% rename from mepo.d/command/stash/show/show.py rename to src/mepo/command/stash/show/show.py diff --git a/mepo.d/command/stash/stash.py b/src/mepo/command/stash/stash.py similarity index 100% rename from mepo.d/command/stash/stash.py rename to src/mepo/command/stash/stash.py diff --git a/mepo.d/command/status/status.py b/src/mepo/command/status/status.py similarity index 100% rename from mepo.d/command/status/status.py rename to src/mepo/command/status/status.py diff --git a/mepo.d/command/tag/create/create.py b/src/mepo/command/tag/create/create.py similarity index 100% rename from mepo.d/command/tag/create/create.py rename to src/mepo/command/tag/create/create.py diff --git a/mepo.d/command/tag/delete/delete.py b/src/mepo/command/tag/delete/delete.py similarity index 100% rename from mepo.d/command/tag/delete/delete.py rename to src/mepo/command/tag/delete/delete.py diff --git a/mepo.d/command/tag/list/list.py b/src/mepo/command/tag/list/list.py similarity index 100% rename from mepo.d/command/tag/list/list.py rename to src/mepo/command/tag/list/list.py diff --git a/mepo.d/command/tag/push/push.py b/src/mepo/command/tag/push/push.py similarity index 100% rename from mepo.d/command/tag/push/push.py rename to src/mepo/command/tag/push/push.py diff --git a/mepo.d/command/tag/tag.py b/src/mepo/command/tag/tag.py similarity index 100% rename from mepo.d/command/tag/tag.py rename to src/mepo/command/tag/tag.py diff --git a/mepo.d/command/unstage/unstage.py b/src/mepo/command/unstage/unstage.py similarity index 100% rename from mepo.d/command/unstage/unstage.py rename to src/mepo/command/unstage/unstage.py diff --git a/mepo.d/command/whereis/whereis.py b/src/mepo/command/whereis/whereis.py similarity index 100% rename from mepo.d/command/whereis/whereis.py rename to src/mepo/command/whereis/whereis.py diff --git a/mepo.d/config/config_file.py b/src/mepo/config/config_file.py similarity index 100% rename from mepo.d/config/config_file.py rename to src/mepo/config/config_file.py diff --git a/mepo.d/main.py b/src/mepo/main.py similarity index 100% rename from mepo.d/main.py rename to src/mepo/main.py diff --git a/mepo.d/repository/git.py b/src/mepo/repository/git.py similarity index 100% rename from mepo.d/repository/git.py rename to src/mepo/repository/git.py diff --git a/mepo.d/state/component.py b/src/mepo/state/component.py similarity index 100% rename from mepo.d/state/component.py rename to src/mepo/state/component.py diff --git a/mepo.d/state/exceptions.py b/src/mepo/state/exceptions.py similarity index 100% rename from mepo.d/state/exceptions.py rename to src/mepo/state/exceptions.py diff --git a/mepo.d/state/state.py b/src/mepo/state/state.py similarity index 100% rename from mepo.d/state/state.py rename to src/mepo/state/state.py diff --git a/mepo.d/utest/input/args.py b/src/mepo/utest/input/args.py similarity index 100% rename from mepo.d/utest/input/args.py rename to src/mepo/utest/input/args.py diff --git a/mepo.d/utest/input/components.yaml b/src/mepo/utest/input/components.yaml similarity index 100% rename from mepo.d/utest/input/components.yaml rename to src/mepo/utest/input/components.yaml diff --git a/mepo.d/utest/output/compare_brief_output.txt b/src/mepo/utest/output/compare_brief_output.txt similarity index 100% rename from mepo.d/utest/output/compare_brief_output.txt rename to src/mepo/utest/output/compare_brief_output.txt diff --git a/mepo.d/utest/output/compare_full_output.txt b/src/mepo/utest/output/compare_full_output.txt similarity index 100% rename from mepo.d/utest/output/compare_full_output.txt rename to src/mepo/utest/output/compare_full_output.txt diff --git a/mepo.d/utest/output/list_output.txt b/src/mepo/utest/output/list_output.txt similarity index 100% rename from mepo.d/utest/output/list_output.txt rename to src/mepo/utest/output/list_output.txt diff --git a/mepo.d/utest/output/status_output.txt b/src/mepo/utest/output/status_output.txt similarity index 100% rename from mepo.d/utest/output/status_output.txt rename to src/mepo/utest/output/status_output.txt diff --git a/mepo.d/utest/test_mepo_commands.py b/src/mepo/utest/test_mepo_commands.py similarity index 100% rename from mepo.d/utest/test_mepo_commands.py rename to src/mepo/utest/test_mepo_commands.py diff --git a/mepo.d/utilities/colors.py b/src/mepo/utilities/colors.py similarity index 100% rename from mepo.d/utilities/colors.py rename to src/mepo/utilities/colors.py diff --git a/mepo.d/utilities/mepoconfig.py b/src/mepo/utilities/mepoconfig.py similarity index 100% rename from mepo.d/utilities/mepoconfig.py rename to src/mepo/utilities/mepoconfig.py diff --git a/mepo.d/utilities/shellcmd.py b/src/mepo/utilities/shellcmd.py similarity index 100% rename from mepo.d/utilities/shellcmd.py rename to src/mepo/utilities/shellcmd.py diff --git a/mepo.d/utilities/verify.py b/src/mepo/utilities/verify.py similarity index 100% rename from mepo.d/utilities/verify.py rename to src/mepo/utilities/verify.py diff --git a/mepo.d/utilities/version.py b/src/mepo/utilities/version.py similarity index 100% rename from mepo.d/utilities/version.py rename to src/mepo/utilities/version.py From 3443483b3162adf927958166c6951c3b3618f170 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 09:24:11 -0500 Subject: [PATCH 02/27] Adjusted paths for importing modules --- src/mepo/cmdline/parser.py | 10 +++++----- src/mepo/command/branch/branch.py | 8 ++++---- src/mepo/command/branch/create/create.py | 6 +++--- src/mepo/command/branch/delete/delete.py | 6 +++--- src/mepo/command/branch/list/list.py | 6 +++--- src/mepo/command/changed-files/changed-files.py | 15 ++++++++------- .../checkout-if-exists/checkout-if-exists.py | 8 ++++---- src/mepo/command/checkout/checkout.py | 8 ++++---- src/mepo/command/clone/clone.py | 12 ++++++------ src/mepo/command/command.py | 4 ++-- src/mepo/command/commit/commit.py | 8 ++++---- src/mepo/command/compare/compare.py | 9 +++++---- src/mepo/command/config/config.py | 10 +++++----- src/mepo/command/config/delete/delete.py | 2 +- src/mepo/command/config/get/get.py | 2 +- src/mepo/command/config/print/print.py | 2 +- src/mepo/command/config/set/set.py | 2 +- src/mepo/command/develop/develop.py | 8 ++++---- src/mepo/command/diff/diff.py | 8 ++++---- src/mepo/command/fetch/fetch.py | 8 ++++---- src/mepo/command/init/init.py | 4 ++-- src/mepo/command/list/list.py | 2 +- src/mepo/command/pull-all/pull-all.py | 8 ++++---- src/mepo/command/pull/pull.py | 10 +++++----- src/mepo/command/push/push.py | 6 +++--- src/mepo/command/reset/reset.py | 6 +++--- src/mepo/command/restore-state/restore-state.py | 8 ++++---- src/mepo/command/save/save.py | 10 +++++----- src/mepo/command/stage/stage.py | 8 ++++---- src/mepo/command/stash/apply/apply.py | 6 +++--- src/mepo/command/stash/list/list.py | 4 ++-- src/mepo/command/stash/pop/pop.py | 6 +++--- src/mepo/command/stash/push/push.py | 6 +++--- src/mepo/command/stash/show/show.py | 6 +++--- src/mepo/command/stash/stash.py | 12 ++++++------ src/mepo/command/status/status.py | 12 ++++++------ src/mepo/command/tag/create/create.py | 6 +++--- src/mepo/command/tag/delete/delete.py | 6 +++--- src/mepo/command/tag/list/list.py | 6 +++--- src/mepo/command/tag/push/push.py | 6 +++--- src/mepo/command/tag/tag.py | 10 +++++----- src/mepo/command/unstage/unstage.py | 8 ++++---- src/mepo/command/whereis/whereis.py | 4 ++-- src/mepo/config/config_file.py | 2 +- src/mepo/repository/git.py | 9 +++++---- src/mepo/state/component.py | 6 ++++-- src/mepo/state/state.py | 9 +++++---- 47 files changed, 167 insertions(+), 161 deletions(-) diff --git a/src/mepo/cmdline/parser.py b/src/mepo/cmdline/parser.py index 34f9045..237cc04 100644 --- a/src/mepo/cmdline/parser.py +++ b/src/mepo/cmdline/parser.py @@ -1,10 +1,10 @@ import argparse -from cmdline.branch_parser import MepoBranchArgParser -from cmdline.stash_parser import MepoStashArgParser -from cmdline.tag_parser import MepoTagArgParser -from cmdline.config_parser import MepoConfigArgParser -from utilities import mepoconfig +from mepo.cmdline.branch_parser import MepoBranchArgParser +from mepo.cmdline.stash_parser import MepoStashArgParser +from mepo.cmdline.tag_parser import MepoTagArgParser +from mepo.cmdline.config_parser import MepoConfigArgParser +from mepo.utilities import mepoconfig class MepoArgParser(object): diff --git a/src/mepo/command/branch/branch.py b/src/mepo/command/branch/branch.py index 06b3ec1..961465c 100644 --- a/src/mepo/command/branch/branch.py +++ b/src/mepo/command/branch/branch.py @@ -1,10 +1,10 @@ import subprocess as sp -from state.state import MepoState +from mepo.state.state import MepoState -from command.branch.list import list -from command.branch.create import create -from command.branch.delete import delete +from mepo.command.branch.list import list +from mepo.command.branch.create import create +from mepo.command.branch.delete import delete def run(args): d = { diff --git a/src/mepo/command/branch/create/create.py b/src/mepo/command/branch/create/create.py index d6772a6..ba17415 100644 --- a/src/mepo/command/branch/create/create.py +++ b/src/mepo/command/branch/create/create.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/branch/delete/delete.py b/src/mepo/command/branch/delete/delete.py index 3206b3d..dc60bc9 100644 --- a/src/mepo/command/branch/delete/delete.py +++ b/src/mepo/command/branch/delete/delete.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/branch/list/list.py b/src/mepo/command/branch/list/list.py index 7174024..16ce794 100644 --- a/src/mepo/command/branch/list/list.py +++ b/src/mepo/command/branch/list/list.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/changed-files/changed-files.py b/src/mepo/command/changed-files/changed-files.py index c2a1e2a..4ae24ae 100644 --- a/src/mepo/command/changed-files/changed-files.py +++ b/src/mepo/command/changed-files/changed-files.py @@ -1,11 +1,12 @@ -from state.state import MepoState -from utilities import colors -from utilities import verify -from utilities.version import version_to_string, sanitize_version_string -from repository.git import GitRepository -from shutil import get_terminal_size -from state.component import MepoVersion import os +from shutil import get_terminal_size + +from mepo.state.state import MepoState +from mepo.state.component import MepoVersion +from mepo.utilities import colors +from mepo.utilities import verify +from mepo.utilities.version import version_to_string, sanitize_version_string +from mepo.repository.git import GitRepository VER_LEN = 30 diff --git a/src/mepo/command/checkout-if-exists/checkout-if-exists.py b/src/mepo/command/checkout-if-exists/checkout-if-exists.py index 528afb1..dfea26c 100644 --- a/src/mepo/command/checkout-if-exists/checkout-if-exists.py +++ b/src/mepo/command/checkout-if-exists/checkout-if-exists.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from utilities import colors +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/checkout/checkout.py b/src/mepo/command/checkout/checkout.py index 0e18eb4..6e16ba1 100644 --- a/src/mepo/command/checkout/checkout.py +++ b/src/mepo/command/checkout/checkout.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from utilities import colors +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/clone/clone.py b/src/mepo/command/clone/clone.py index 448e284..5f0033a 100644 --- a/src/mepo/command/clone/clone.py +++ b/src/mepo/command/clone/clone.py @@ -1,13 +1,13 @@ -from state.state import MepoState, StateDoesNotExistError -from repository.git import GitRepository -from command.init import init as mepo_init -from utilities import shellcmd, colors, mepoconfig -from urllib.parse import urlparse - import os import pathlib import shutil import shlex +from urllib.parse import urlparse + +from mepo.state.state import MepoState, StateDoesNotExistError +from mepo.repository.git import GitRepository +from mepo.command.init import init as mepo_init +from mepo.utilities import shellcmd, colors, mepoconfig def run(args): diff --git a/src/mepo/command/command.py b/src/mepo/command/command.py index 221c926..8a6a509 100644 --- a/src/mepo/command/command.py +++ b/src/mepo/command/command.py @@ -1,11 +1,11 @@ from importlib import import_module -from utilities import mepoconfig +from mepo.utilities import mepoconfig def run(args): mepo_cmd = mepoconfig.get_alias_command(args.mepo_cmd) # Load the module containing the 'run' method of specified mepo command - cmd_module = import_module('command.{}.{}'.format(mepo_cmd, mepo_cmd)) + cmd_module = import_module('mepo.command.{}.{}'.format(mepo_cmd, mepo_cmd)) # Execute 'run' method of the specified mepo command cmd_module.run(args) diff --git a/src/mepo/command/commit/commit.py b/src/mepo/command/commit/commit.py index 7b2ac34..f91c127 100644 --- a/src/mepo/command/commit/commit.py +++ b/src/mepo/command/commit/commit.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from command.stage.stage import stage_files +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.command.stage.stage import stage_files # Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 import os, tempfile, subprocess diff --git a/src/mepo/command/compare/compare.py b/src/mepo/command/compare/compare.py index 9f78165..f16348a 100644 --- a/src/mepo/command/compare/compare.py +++ b/src/mepo/command/compare/compare.py @@ -1,9 +1,10 @@ -from state.state import MepoState -from utilities import colors -from utilities.version import version_to_string, sanitize_version_string -from repository.git import GitRepository from shutil import get_terminal_size +from mepo.state.state import MepoState +from mepo.utilities import colors +from mepo.utilities.version import version_to_string, sanitize_version_string +from mepo.repository.git import GitRepository + VER_LEN = 30 def run(args): diff --git a/src/mepo/command/config/config.py b/src/mepo/command/config/config.py index 689ccaa..211b7da 100644 --- a/src/mepo/command/config/config.py +++ b/src/mepo/command/config/config.py @@ -1,11 +1,11 @@ import subprocess as sp -from state.state import MepoState +from mepo.state.state import MepoState -from command.config.get import get -from command.config.set import set -from command.config.delete import delete -from command.config.print import print +from mepo.command.config.get import get +from mepo.command.config.set import set +from mepo.command.config.delete import delete +from mepo.command.config.print import print def run(args): d = { diff --git a/src/mepo/command/config/delete/delete.py b/src/mepo/command/config/delete/delete.py index dbf58da..b7b85b2 100644 --- a/src/mepo/command/config/delete/delete.py +++ b/src/mepo/command/config/delete/delete.py @@ -1,4 +1,4 @@ -from utilities import mepoconfig +from mepo.utilities import mepoconfig def run(args): section, option = mepoconfig.split_entry(args.entry) diff --git a/src/mepo/command/config/get/get.py b/src/mepo/command/config/get/get.py index 6ca8346..e88d145 100644 --- a/src/mepo/command/config/get/get.py +++ b/src/mepo/command/config/get/get.py @@ -1,4 +1,4 @@ -from utilities import mepoconfig +from mepo.utilities import mepoconfig def run(args): section, option = mepoconfig.split_entry(args.entry) diff --git a/src/mepo/command/config/print/print.py b/src/mepo/command/config/print/print.py index 16d85c6..1a612c3 100644 --- a/src/mepo/command/config/print/print.py +++ b/src/mepo/command/config/print/print.py @@ -1,4 +1,4 @@ -from utilities import mepoconfig +from mepo.utilities import mepoconfig def run(args): mepoconfig.print() diff --git a/src/mepo/command/config/set/set.py b/src/mepo/command/config/set/set.py index 6ac0a4c..80d9079 100644 --- a/src/mepo/command/config/set/set.py +++ b/src/mepo/command/config/set/set.py @@ -1,4 +1,4 @@ -from utilities import mepoconfig +from mepo.utilities import mepoconfig def run(args): section, option = mepoconfig.split_entry(args.entry) diff --git a/src/mepo/command/develop/develop.py b/src/mepo/command/develop/develop.py index 8c2aa92..aec6f28 100644 --- a/src/mepo/command/develop/develop.py +++ b/src/mepo/command/develop/develop.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from utilities import colors +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/diff/diff.py b/src/mepo/command/diff/diff.py index 8353196..6afcd40 100644 --- a/src/mepo/command/diff/diff.py +++ b/src/mepo/command/diff/diff.py @@ -3,12 +3,12 @@ import multiprocessing as mp import os -from state.state import MepoState -from repository.git import GitRepository -from utilities import verify - from shutil import get_terminal_size +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository +from mepo.utilities import verify + def run(args): foundDiff = False diff --git a/src/mepo/command/fetch/fetch.py b/src/mepo/command/fetch/fetch.py index cddbbcb..299db8c 100644 --- a/src/mepo/command/fetch/fetch.py +++ b/src/mepo/command/fetch/fetch.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from utilities import colors +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/init/init.py b/src/mepo/command/init/init.py index c868f08..b4ef212 100644 --- a/src/mepo/command/init/init.py +++ b/src/mepo/command/init/init.py @@ -1,5 +1,5 @@ -from state.state import MepoState -from utilities import mepoconfig +from mepo.state.state import MepoState +from mepo.utilities import mepoconfig def run(args): if args.style: diff --git a/src/mepo/command/list/list.py b/src/mepo/command/list/list.py index d60e932..25a8e4d 100644 --- a/src/mepo/command/list/list.py +++ b/src/mepo/command/list/list.py @@ -1,4 +1,4 @@ -from state.state import MepoState +from mepo.state.state import MepoState def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/pull-all/pull-all.py b/src/mepo/command/pull-all/pull-all.py index c902b56..a47bf69 100644 --- a/src/mepo/command/pull-all/pull-all.py +++ b/src/mepo/command/pull-all/pull-all.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from repository.git import GitRepository -from state.component import MepoVersion -from utilities import colors +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository +from mepo.state.component import MepoVersion +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/pull/pull.py b/src/mepo/command/pull/pull.py index 92aa0bf..f934e80 100644 --- a/src/mepo/command/pull/pull.py +++ b/src/mepo/command/pull/pull.py @@ -1,8 +1,8 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from state.component import MepoVersion -from utilities import colors +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.state.component import MepoVersion +from mepo.utilities import colors def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/push/push.py b/src/mepo/command/push/push.py index adcf2ae..3b7d27a 100644 --- a/src/mepo/command/push/push.py +++ b/src/mepo/command/push/push.py @@ -1,6 +1,6 @@ -from utilities import verify -from state.state import MepoState -from repository.git import GitRepository +from mepo.utilities import verify +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/reset/reset.py b/src/mepo/command/reset/reset.py index 41cf79e..7ea0104 100644 --- a/src/mepo/command/reset/reset.py +++ b/src/mepo/command/reset/reset.py @@ -1,10 +1,10 @@ import os import shutil -from state.state import MepoState -from state.exceptions import NotInRootDirError +from mepo.state.state import MepoState +from mepo.state.exceptions import NotInRootDirError -from command.clone import clone as mepo_clone +from mepo.command.clone import clone as mepo_clone # This command will "reset" the mepo clone. This will delete all # the subrepos, remove the .mepo directory, and then re-clone all the diff --git a/src/mepo/command/restore-state/restore-state.py b/src/mepo/command/restore-state/restore-state.py index 38a124b..b713bf3 100644 --- a/src/mepo/command/restore-state/restore-state.py +++ b/src/mepo/command/restore-state/restore-state.py @@ -3,10 +3,10 @@ import multiprocessing as mp import atexit -from state.state import MepoState -from repository.git import GitRepository -from utilities.version import version_to_string -from utilities import colors +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository +from mepo.utilities.version import version_to_string +from mepo.utilities import colors def run(args): print('Checking status...'); sys.stdout.flush() diff --git a/src/mepo/command/save/save.py b/src/mepo/command/save/save.py index 336b3f7..44f3edf 100644 --- a/src/mepo/command/save/save.py +++ b/src/mepo/command/save/save.py @@ -1,8 +1,8 @@ -from state.state import MepoState -from state.component import MepoVersion -from repository.git import GitRepository -from config.config_file import ConfigFile -from utilities.version import sanitize_version_string +from mepo.state.state import MepoState +from mepo.state.component import MepoVersion +from mepo.repository.git import GitRepository +from mepo.config.config_file import ConfigFile +from mepo.utilities.version import sanitize_version_string import os diff --git a/src/mepo/command/stage/stage.py b/src/mepo/command/stage/stage.py index ea93d9a..5f7d3d7 100644 --- a/src/mepo/command/stage/stage.py +++ b/src/mepo/command/stage/stage.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository -from state.component import MepoVersion +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository +from mepo.state.component import MepoVersion def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/apply/apply.py b/src/mepo/command/stash/apply/apply.py index 04f7e37..564b034 100644 --- a/src/mepo/command/stash/apply/apply.py +++ b/src/mepo/command/stash/apply/apply.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/list/list.py b/src/mepo/command/stash/list/list.py index 62eb0d9..d7f0510 100644 --- a/src/mepo/command/stash/list/list.py +++ b/src/mepo/command/stash/list/list.py @@ -1,5 +1,5 @@ -from state.state import MepoState -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/pop/pop.py b/src/mepo/command/stash/pop/pop.py index 12f79da..ff9a2fc 100644 --- a/src/mepo/command/stash/pop/pop.py +++ b/src/mepo/command/stash/pop/pop.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/push/push.py b/src/mepo/command/stash/push/push.py index 276a95e..e806e0b 100644 --- a/src/mepo/command/stash/push/push.py +++ b/src/mepo/command/stash/push/push.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/show/show.py b/src/mepo/command/stash/show/show.py index 69f3766..fda6ee3 100644 --- a/src/mepo/command/stash/show/show.py +++ b/src/mepo/command/stash/show/show.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/stash/stash.py b/src/mepo/command/stash/stash.py index 05d6575..fe0ef3e 100644 --- a/src/mepo/command/stash/stash.py +++ b/src/mepo/command/stash/stash.py @@ -1,12 +1,12 @@ import subprocess as sp -from state.state import MepoState +from mepo.state.state import MepoState -from command.stash.list import list -from command.stash.pop import pop -from command.stash.apply import apply -from command.stash.push import push -from command.stash.show import show +from mepo.command.stash.list import list +from mepo.command.stash.pop import pop +from mepo.command.stash.apply import apply +from mepo.command.stash.push import push +from mepo.command.stash.show import show def run(args): d = { diff --git a/src/mepo/command/status/status.py b/src/mepo/command/status/status.py index 9172059..9f7bc91 100644 --- a/src/mepo/command/status/status.py +++ b/src/mepo/command/status/status.py @@ -2,14 +2,14 @@ import time import multiprocessing as mp import atexit - -from state.state import MepoState -from repository.git import GitRepository -from utilities.version import version_to_string, sanitize_version_string -from utilities import colors, shellcmd -from command.whereis.whereis import _get_relative_path import shlex +from mepo.state.state import MepoState +from mepo.repository.git import GitRepository +from mepo.utilities.version import version_to_string, sanitize_version_string +from mepo.utilities import colors, shellcmd +from mepo.command.whereis.whereis import _get_relative_path + def run(args): print('Checking status...'); sys.stdout.flush() allcomps = MepoState.read_state() diff --git a/src/mepo/command/tag/create/create.py b/src/mepo/command/tag/create/create.py index cc765d4..aca4e3c 100644 --- a/src/mepo/command/tag/create/create.py +++ b/src/mepo/command/tag/create/create.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository # Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 import os, tempfile, subprocess diff --git a/src/mepo/command/tag/delete/delete.py b/src/mepo/command/tag/delete/delete.py index f063e6a..8998f4c 100644 --- a/src/mepo/command/tag/delete/delete.py +++ b/src/mepo/command/tag/delete/delete.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/tag/list/list.py b/src/mepo/command/tag/list/list.py index c04c325..a7c2a98 100644 --- a/src/mepo/command/tag/list/list.py +++ b/src/mepo/command/tag/list/list.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/tag/push/push.py b/src/mepo/command/tag/push/push.py index 975c2e1..e428ed1 100644 --- a/src/mepo/command/tag/push/push.py +++ b/src/mepo/command/tag/push/push.py @@ -1,6 +1,6 @@ -from state.state import MepoState -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/tag/tag.py b/src/mepo/command/tag/tag.py index 7158f1b..9b02fe8 100644 --- a/src/mepo/command/tag/tag.py +++ b/src/mepo/command/tag/tag.py @@ -1,11 +1,11 @@ import subprocess as sp -from state.state import MepoState +from mepo.state.state import MepoState -from command.tag.list import list -from command.tag.create import create -from command.tag.delete import delete -from command.tag.push import push +from mepo.command.tag.list import list +from mepo.command.tag.create import create +from mepo.command.tag.delete import delete +from mepo.command.tag.push import push def run(args): d = { diff --git a/src/mepo/command/unstage/unstage.py b/src/mepo/command/unstage/unstage.py index 76e726b..d0f9600 100644 --- a/src/mepo/command/unstage/unstage.py +++ b/src/mepo/command/unstage/unstage.py @@ -1,7 +1,7 @@ -from state.state import MepoState -from utilities import shellcmd -from utilities import verify -from repository.git import GitRepository +from mepo.state.state import MepoState +from mepo.utilities import shellcmd +from mepo.utilities import verify +from mepo.repository.git import GitRepository def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/command/whereis/whereis.py b/src/mepo/command/whereis/whereis.py index df6c043..c5da609 100644 --- a/src/mepo/command/whereis/whereis.py +++ b/src/mepo/command/whereis/whereis.py @@ -1,7 +1,7 @@ import os -from state.state import MepoState -from utilities import verify +from mepo.state.state import MepoState +from mepo.utilities import verify def run(args): allcomps = MepoState.read_state() diff --git a/src/mepo/config/config_file.py b/src/mepo/config/config_file.py index c72ee3a..b4c1089 100644 --- a/src/mepo/config/config_file.py +++ b/src/mepo/config/config_file.py @@ -1,7 +1,7 @@ import pathlib import yaml -from state.exceptions import SuffixNotRecognizedError +from mepo.state.exceptions import SuffixNotRecognizedError # From https://github.com/yaml/pyyaml/issues/127#issuecomment-525800484 class AddBlankLinesDumper(yaml.SafeDumper): diff --git a/src/mepo/repository/git.py b/src/mepo/repository/git.py index b54b54b..e3a566a 100644 --- a/src/mepo/repository/git.py +++ b/src/mepo/repository/git.py @@ -3,11 +3,12 @@ import subprocess import shlex -from state.state import MepoState -from utilities import shellcmd -from utilities import colors from urllib.parse import urljoin -from state.exceptions import RepoAlreadyClonedError + +from mepo.state.state import MepoState +from mepo.utilities import shellcmd +from mepo.utilities import colors +from mepo.state.exceptions import RepoAlreadyClonedError class GitRepository(object): """ diff --git a/src/mepo/state/component.py b/src/mepo/state/component.py index e121075..5d79f39 100644 --- a/src/mepo/state/component.py +++ b/src/mepo/state/component.py @@ -1,9 +1,11 @@ import os import shlex import textwrap + from collections import namedtuple -from utilities.version import MepoVersion -from utilities import shellcmd, mepoconfig + +from mepo.utilities.version import MepoVersion +from mepo.utilities import shellcmd, mepoconfig from urllib.parse import urlparse # This will be used to store the "final nodes" from each subrepo diff --git a/src/mepo/state/state.py b/src/mepo/state/state.py index 45ced34..24be351 100644 --- a/src/mepo/state/state.py +++ b/src/mepo/state/state.py @@ -4,11 +4,12 @@ import glob import pickle -from config.config_file import ConfigFile -from state.component import MepoComponent -from utilities import shellcmd from pathlib import Path -from state.exceptions import StateDoesNotExistError, StateAlreadyInitializedError + +from mepo.config.config_file import ConfigFile +from mepo.state.component import MepoComponent +from mepo.utilities import shellcmd +from mepo.state.exceptions import StateDoesNotExistError, StateAlreadyInitializedError class MepoState(object): From e2d253f17d1e418c1a03bf3c454af965aa82c645 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 09:25:34 -0500 Subject: [PATCH 03/27] Renamed doc -> docs --- {doc => docs}/.gitignore | 0 {doc => docs}/make_md_docs.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {doc => docs}/.gitignore (100%) rename {doc => docs}/make_md_docs.py (100%) diff --git a/doc/.gitignore b/docs/.gitignore similarity index 100% rename from doc/.gitignore rename to docs/.gitignore diff --git a/doc/make_md_docs.py b/docs/make_md_docs.py similarity index 100% rename from doc/make_md_docs.py rename to docs/make_md_docs.py From d513f892cb0e7731387a39f7aceb3d2a2650b558 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 09:44:51 -0500 Subject: [PATCH 04/27] Moved test directory out of src --- {src/mepo/utest => tests}/input/args.py | 0 {src/mepo/utest => tests}/input/components.yaml | 0 .../utest => tests}/output/compare_brief_output.txt | 0 .../utest => tests}/output/compare_full_output.txt | 0 {src/mepo/utest => tests}/output/list_output.txt | 0 {src/mepo/utest => tests}/output/status_output.txt | 0 {src/mepo/utest => tests}/test_mepo_commands.py | 12 ++++++------ 7 files changed, 6 insertions(+), 6 deletions(-) rename {src/mepo/utest => tests}/input/args.py (100%) rename {src/mepo/utest => tests}/input/components.yaml (100%) rename {src/mepo/utest => tests}/output/compare_brief_output.txt (100%) rename {src/mepo/utest => tests}/output/compare_full_output.txt (100%) rename {src/mepo/utest => tests}/output/list_output.txt (100%) rename {src/mepo/utest => tests}/output/status_output.txt (100%) rename {src/mepo/utest => tests}/test_mepo_commands.py (91%) diff --git a/src/mepo/utest/input/args.py b/tests/input/args.py similarity index 100% rename from src/mepo/utest/input/args.py rename to tests/input/args.py diff --git a/src/mepo/utest/input/components.yaml b/tests/input/components.yaml similarity index 100% rename from src/mepo/utest/input/components.yaml rename to tests/input/components.yaml diff --git a/src/mepo/utest/output/compare_brief_output.txt b/tests/output/compare_brief_output.txt similarity index 100% rename from src/mepo/utest/output/compare_brief_output.txt rename to tests/output/compare_brief_output.txt diff --git a/src/mepo/utest/output/compare_full_output.txt b/tests/output/compare_full_output.txt similarity index 100% rename from src/mepo/utest/output/compare_full_output.txt rename to tests/output/compare_full_output.txt diff --git a/src/mepo/utest/output/list_output.txt b/tests/output/list_output.txt similarity index 100% rename from src/mepo/utest/output/list_output.txt rename to tests/output/list_output.txt diff --git a/src/mepo/utest/output/status_output.txt b/tests/output/status_output.txt similarity index 100% rename from src/mepo/utest/output/status_output.txt rename to tests/output/status_output.txt diff --git a/src/mepo/utest/test_mepo_commands.py b/tests/test_mepo_commands.py similarity index 91% rename from src/mepo/utest/test_mepo_commands.py rename to tests/test_mepo_commands.py index c1fc5a6..3436486 100644 --- a/src/mepo/utest/test_mepo_commands.py +++ b/tests/test_mepo_commands.py @@ -10,12 +10,12 @@ from input import args -from command.init import init as mepo_init -from command.clone import clone as mepo_clone -from command.list import list as mepo_list -from command.status import status as mepo_status -from command.compare import compare as mepo_compare -from command.develop import develop as mepo_develop +from mepo.command.init import init as mepo_init +from mepo.command.clone import clone as mepo_clone +from mepo.command.list import list as mepo_list +from mepo.command.status import status as mepo_status +from mepo.command.compare import compare as mepo_compare +from mepo.command.develop import develop as mepo_develop class TestMepoCommands(unittest.TestCase): From 277ba9dc9e7ea832e52fd9048f02ad15edc5fa3b Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 09:45:35 -0500 Subject: [PATCH 05/27] Updated path in top-level mepo script --- mepo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mepo b/mepo index c53176f..426a8c9 100755 --- a/mepo +++ b/mepo @@ -9,7 +9,7 @@ if sys.version_info < (3, 6, 0): sys.exit('ERROR: Python version needs to be >= 3.6.0') # Add mepo.d to path -MEPO_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mepo.d') +MEPO_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'src', 'mepo') sys.path.insert(0, MEPO_D) if __name__ == '__main__': From 3a021d3e3c888769ef229c27b428a892e64a72ec Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 11:24:03 -0500 Subject: [PATCH 06/27] Added pyproject.toml for pip installation --- pyproject.toml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3e6181e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "mepo" +version = "v1.55.0" +authors = ["GMAO SI Team"] +description = "Tool to manage (m)ultiple git r(epo)sitories" +requires-python = ">= 3.9" +license = {file = "LICENSE"} +readme = "README.md" +dependencies = [ + "pyyaml>=6.0.1", +] + +[project.urls] +Homepage = "https://github.com/GEOS-ESM/mepo" + +[project.scripts] +mepo = "mepo:main" + +[tool.setuptools] +include-package-data = true From b9e8c71e5d6e03c1c5fcb61031a883aa8d93360a Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 12 Mar 2024 16:57:32 -0500 Subject: [PATCH 07/27] Some fixes 1. module paths 2. new version number in pyproject.toml 3. location of test script --- .github/workflows/mepo.yaml | 2 +- pyproject.toml | 10 ++++++---- src/mepo/main.py | 4 ++-- tests/test_mepo_commands.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/mepo.yaml b/.github/workflows/mepo.yaml index 4e7710c..50206b9 100644 --- a/.github/workflows/mepo.yaml +++ b/.github/workflows/mepo.yaml @@ -27,5 +27,5 @@ jobs: timeout-minutes: 5 - name: Run unit tests - run: python3 mepo.d/utest/test_mepo_commands.py -v + run: python3 tests/test_mepo_commands.py -v timeout-minutes: 5 diff --git a/pyproject.toml b/pyproject.toml index 3e6181e..02a97b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,9 +4,11 @@ build-backend = "setuptools.build_meta" [project] name = "mepo" -version = "v1.55.0" -authors = ["GMAO SI Team"] -description = "Tool to manage (m)ultiple git r(epo)sitories" +version = "v1.59.0" +authors = [ + {name = "GMAO SI Team", email = "gmao.siteam@nasa.gov"}, +] +description = "Add description here" requires-python = ">= 3.9" license = {file = "LICENSE"} readme = "README.md" @@ -18,7 +20,7 @@ dependencies = [ Homepage = "https://github.com/GEOS-ESM/mepo" [project.scripts] -mepo = "mepo:main" +mepo = "mepo.main:main" [tool.setuptools] include-package-data = true diff --git a/src/mepo/main.py b/src/mepo/main.py index 620c7e5..30a3135 100644 --- a/src/mepo/main.py +++ b/src/mepo/main.py @@ -1,5 +1,5 @@ -from cmdline.parser import MepoArgParser -from command import command +from mepo.cmdline.parser import MepoArgParser +from mepo.command import command def main(): args = MepoArgParser().parse() diff --git a/tests/test_mepo_commands.py b/tests/test_mepo_commands.py index 3436486..dbdd88e 100644 --- a/tests/test_mepo_commands.py +++ b/tests/test_mepo_commands.py @@ -1,7 +1,7 @@ import os import sys THIS_DIR = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(THIS_DIR, '..')) +sys.path.insert(0, os.path.join(THIS_DIR, '..', 'src')) import shutil import shlex import unittest From 6f9608d7a6c357d3a404de2f7010bb3748c19d32 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Wed, 13 Mar 2024 07:42:02 -0500 Subject: [PATCH 08/27] Updated paths for helper script --- mepo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mepo b/mepo index 426a8c9..e5389cc 100755 --- a/mepo +++ b/mepo @@ -8,10 +8,10 @@ import traceback if sys.version_info < (3, 6, 0): sys.exit('ERROR: Python version needs to be >= 3.6.0') -# Add mepo.d to path -MEPO_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'src', 'mepo') -sys.path.insert(0, MEPO_D) +# Add src (containing dir mepo) to path +SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'src') +sys.path.insert(0, SRC_D) if __name__ == '__main__': - from main import main + from mepo.main import main main() From 749869cfd97a2cb499afa9a6bdee7d821063548b Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Wed, 13 Mar 2024 07:42:53 -0500 Subject: [PATCH 09/27] Fixed doc generation --- docs/make_md_docs.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/make_md_docs.py b/docs/make_md_docs.py index c8a1961..2b92747 100644 --- a/docs/make_md_docs.py +++ b/docs/make_md_docs.py @@ -13,12 +13,12 @@ doc_dir_path = os.path.dirname(os.path.realpath(__file__)) # Then we need to get to the mepo/mepo.d/command directory. First the "main" dir main_dir_path = os.path.dirname(doc_dir_path) -# Now add 'mepo.d' -mepod_dir_path = os.path.join(main_dir_path,'mepo.d') +# Now add 'src/mepo' +mepod_dir_path = os.path.join(main_dir_path, 'src', 'mepo') # And then 'command' -command_dir_path = os.path.join(mepod_dir_path,'command') +command_dir_path = os.path.join(mepod_dir_path, 'command') -mepo_command_path = os.path.join(main_dir_path,'mepo') +mepo_command_path = os.path.join(main_dir_path, 'mepo') def get_command_list(directory): # Walk the tree @@ -50,12 +50,12 @@ def get_command_list(directory): return sorted(all_useful_commands) def create_markdown_from_usage(command, mdFile): - cmd = [mepo_command_path,command,'--help'] + cmd = [mepo_command_path, command, '--help'] # Some commands have spaces, so we need to break it up again cmd = ' '.join(cmd).split() - result = sp.run(cmd,capture_output=True,universal_newlines=True,env={'COLUMNS':'256'}) + result = sp.run(cmd, capture_output=True, universal_newlines=True, env={'COLUMNS':'256'}) output = result.stdout output_list = output.split("\n") @@ -84,7 +84,7 @@ def create_markdown_from_usage(command, mdFile): if __name__ == "__main__": - doc_file='Mepo-Commands.md' + doc_file = 'Mepo-Commands.md' mdFile = MdUtils(file_name=doc_file) mdFile.new_header(level=1, title="Overview") @@ -99,4 +99,3 @@ def create_markdown_from_usage(command, mdFile): mdFile.new_table_of_contents(table_title='Table of Contents', depth=2) mdFile.create_md_file() print(f'Generated {doc_file}.') - From 4de9a144063b630c5ddc09437626fe9268dab324 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Thu, 14 Mar 2024 09:13:25 -0500 Subject: [PATCH 10/27] Moved helper script to the bin directory --- mepo => bin/mepo | 4 ++-- docs/make_md_docs.py | 2 +- pyproject.toml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename mepo => bin/mepo (85%) diff --git a/mepo b/bin/mepo similarity index 85% rename from mepo rename to bin/mepo index e5389cc..58bc11d 100755 --- a/mepo +++ b/bin/mepo @@ -8,8 +8,8 @@ import traceback if sys.version_info < (3, 6, 0): sys.exit('ERROR: Python version needs to be >= 3.6.0') -# Add src (containing dir mepo) to path -SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'src') +# Add direactory containing mepo to path +SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src') sys.path.insert(0, SRC_D) if __name__ == '__main__': diff --git a/docs/make_md_docs.py b/docs/make_md_docs.py index 2b92747..07ba6d0 100644 --- a/docs/make_md_docs.py +++ b/docs/make_md_docs.py @@ -18,7 +18,7 @@ # And then 'command' command_dir_path = os.path.join(mepod_dir_path, 'command') -mepo_command_path = os.path.join(main_dir_path, 'mepo') +mepo_command_path = os.path.join(main_dir_path, 'bin', 'mepo') def get_command_list(directory): # Walk the tree diff --git a/pyproject.toml b/pyproject.toml index 02a97b6..e0b106c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta" [project] name = "mepo" -version = "v1.59.0" +version = "v1.60.0" authors = [ - {name = "GMAO SI Team", email = "gmao.siteam@nasa.gov"}, + {name = "GMAO SI Team", email = "siteam@gmao.gsfc.nasa.gov"}, ] -description = "Add description here" +description = "Tool to manage (m)ultiple r(epo)sitories" requires-python = ">= 3.9" license = {file = "LICENSE"} readme = "README.md" From 4e250e11fb7bb4d4dab76f8d9f2e9a54a9385ed5 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Thu, 14 Mar 2024 09:42:47 -0500 Subject: [PATCH 11/27] Updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2f836aa..3ce728f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *~ *.pyc +*.egg-info +dist +venv \ No newline at end of file From 9a043c472981b4764627b58d0a4bcd9501d5c6b5 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 22 Mar 2024 15:53:31 -0500 Subject: [PATCH 12/27] Update required Python version in bin/mepo Co-authored-by: Matthew Thompson --- bin/mepo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/mepo b/bin/mepo index 58bc11d..9037d7d 100755 --- a/bin/mepo +++ b/bin/mepo @@ -5,7 +5,7 @@ import sys import traceback # Version check -if sys.version_info < (3, 6, 0): +if sys.version_info < (3, 9, 0): sys.exit('ERROR: Python version needs to be >= 3.6.0') # Add direactory containing mepo to path From 33017cd457cfa23a6761ef02a320d0bc92f44b71 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 22 Mar 2024 16:20:20 -0500 Subject: [PATCH 13/27] Updated CHANGELOG --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b26be0..844940d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `pyproject.toml` to aid with `pip` installation. + ### Changed -### Removed +- Converted `mepo` to a Python project via the following renaming + -- `mepo.d` -> `src/mepo` + -- `mepo.d/utest` -> `tests` + -- `doc` --> `docs` +- Helper script `mepo`, used for development, moved to the `bin` directory. ## [1.52.0] - 2024-01-10 From 5027609f545f72bfd008fe02d2ebfe55f87510dc Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 22 Mar 2024 16:20:36 -0500 Subject: [PATCH 14/27] This release should probably be 2.0.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e0b106c..0379e92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mepo" -version = "v1.60.0" +version = "v2.0.0" authors = [ {name = "GMAO SI Team", email = "siteam@gmao.gsfc.nasa.gov"}, ] From 809e58b301a9d614a010b3dbb3b573d455200cd0 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 25 Mar 2024 08:48:49 -0400 Subject: [PATCH 15/27] Fix small typo --- bin/mepo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/mepo b/bin/mepo index 9037d7d..fb10b0b 100755 --- a/bin/mepo +++ b/bin/mepo @@ -6,7 +6,7 @@ import traceback # Version check if sys.version_info < (3, 9, 0): - sys.exit('ERROR: Python version needs to be >= 3.6.0') + sys.exit('ERROR: Python version needs to be >= 3.9.0') # Add direactory containing mepo to path SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src') From ba68a314639161bde5e93d5405858dfda1233932 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 30 Apr 2024 10:36:56 -0400 Subject: [PATCH 16/27] Patch for reading mepo1 state --- src/mepo/state/state.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/mepo/state/state.py b/src/mepo/state/state.py index 24be351..0437f12 100644 --- a/src/mepo/state/state.py +++ b/src/mepo/state/state.py @@ -75,8 +75,23 @@ def initialize(cls, project_config_file, directory_style): def read_state(cls): if not cls.exists(): raise StateDoesNotExistError('Error! mepo state does not exist') - with open(cls.get_file(), 'rb') as fin: - allcomps = pickle.load(fin) + try: + with open(cls.get_file(), 'rb') as fin: + allcomps = pickle.load(fin) + except ModuleNotFoundError: + # Patch start + # mepo1 to mepo2 includes reanming + # mepo.d/state/state.py -> src/mepo/state/state.py + # Since pickle requires that "the class definition must be importable + # and live in the same module as when the object was stored", we need to + # patch sys.modules to be able to read mepo1 state + import mepo.state + import mepo.utilities + sys.modules['state'] = mepo.state + sys.modules['utilities'] = mepo.utilities + # Patch end + with open(cls.get_file(), 'rb') as fin: + allcomps = pickle.load(fin) return allcomps @classmethod From 809d1aee4252f444e7eca3f7e8b457d060918244 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Wed, 1 May 2024 10:07:58 -0400 Subject: [PATCH 17/27] Cleaner code for patching in case of mepo1 state + a warning --- src/mepo/state/state.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/mepo/state/state.py b/src/mepo/state/state.py index 0437f12..6ec4d71 100644 --- a/src/mepo/state/state.py +++ b/src/mepo/state/state.py @@ -9,6 +9,7 @@ from mepo.config.config_file import ConfigFile from mepo.state.component import MepoComponent from mepo.utilities import shellcmd +from mepo.utilities import colors from mepo.state.exceptions import StateDoesNotExistError, StateAlreadyInitializedError class MepoState(object): @@ -71,6 +72,21 @@ def initialize(cls, project_config_file, directory_style): complist.append(MepoComponent().to_component(name, comp, directory_style)) cls.write_state(complist) + @staticmethod + def __mepo1_patch(): + """ + mepo1 to mepo2 includes reanming of directories + mepo.d/state/state.py -> src/mepo/state/state.py + Since pickle requires that "the class definition must be importable + and live in the same module as when the object was stored", we need to + patch sys.modules to be able to read mepo1 state + """ + print(colors.YELLOW + 'Converting mepo1 state to mepo2 state' + colors.RESET) + import mepo.state + import mepo.utilities + sys.modules['state'] = mepo.state + sys.modules['utilities'] = mepo.utilities + @classmethod def read_state(cls): if not cls.exists(): @@ -79,17 +95,7 @@ def read_state(cls): with open(cls.get_file(), 'rb') as fin: allcomps = pickle.load(fin) except ModuleNotFoundError: - # Patch start - # mepo1 to mepo2 includes reanming - # mepo.d/state/state.py -> src/mepo/state/state.py - # Since pickle requires that "the class definition must be importable - # and live in the same module as when the object was stored", we need to - # patch sys.modules to be able to read mepo1 state - import mepo.state - import mepo.utilities - sys.modules['state'] = mepo.state - sys.modules['utilities'] = mepo.utilities - # Patch end + cls.__mepo1_patch() with open(cls.get_file(), 'rb') as fin: allcomps = pickle.load(fin) return allcomps From 53ffe8ade026abeb189de43cd422d8e31e382f0e Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Wed, 15 May 2024 08:27:30 -0500 Subject: [PATCH 18/27] Python package with expanded testing (#278) * Code reorganization to address #273 * Fixed issues with renaming config_file as registry * Removed a print statement left in by mistake * On our way to more robust testing * Added nocolor option to 'git branch list' * It's enough to chdir to the fixture dir at the beginning of a test * Added 'mepo branch' tests * Added tests for mepo tag, fetch, pull, pull-all * multiprocessing library supports the context management protocol * Added tests for mepo push, diff, whereis and reset * component.py - using Python3's relative import * Suppressing test outputs to terminal * Turning off testing 'mepo reset' and checking why 'mepo diff' is failing Maybe 'mepo reset' is causing issues with testing 'mepo tag create' * For testing 'mepo diff', ignore the last line '---' * Ignore the last line of both stdout and saved outputs * Escaping the string message of 'git tag create' * Create regular (non-annotated) tag instead * Testing 'mepo reset' is back on - this was not the issue * Code from src/mepo/command/command.py included in src/mepo/__main__.py * Small edits * Updated GEOSfvdycore version for testing * Minor quote changes * Using SimpleNamespace to construct arguments to mepo commands * Multiprocessing lib supports context manager protocol * Removed tests/input/args.py - using SimpleNamespace now * Updated README and CHANGELOG * Moved duplicate code to get GIT_EDITOR, in commit.py and tag_create.py, to git.py * Lint code as part of GitHub actions workflow only for the case where OS is ubuntu-latest and Python version is 3.9 * Added __init__.py in cmdline, command and utilities directories * Linting updates 1. Linting is a separate job now 2. Disabling some error codes, for now 3. Always return a zero status code * Hardcoding Python version * Renaming * Adding ability to create a mepo binary that can uploaded as an artifact of a Github actions workflow. Pyinstaller requires that the top level script not use relative imports * Added fix for reading mepo1 state * Added a mepo command, update-state, to permanently update mepo1 state to mepo2 * Formatting changes to update-state.py * Slightly different printable representation of component * Added an optional argument one-per-line for 'list' * component.py::MepoComponent - renamed to_dict to to_registry_format. Added to_dict * Fixed test test_list * Fixed a bug in update-state - need to switch to the fixture dir first * Implemented a context manager for os.chdir * Reformatted using black * Separate GitHub workflows for running linter and formatter * Added black to requirements * Using poetry to manage dependencies and packaging * Added creation of a top level script to build * Added pylint as (poetry) dependency * Added pylint to requirements.txt * Updated pyproject.toml * Small changes to the test script for consistency in mepo commands * Enabled previously disabled Pylint (C)onvention and (E)rror codes * Added directory containing mepo to PYTHONPATH * Using contextlib.redirect_stdout instead * Can't switch to contextlib.chdir as this exists only in 3.11+ * Run 'black --check' instead. Non-zero return code indicates that some files need to be formatted * Formatted using black * Config file is now called registry * Moved fixture/component validation from MepoComponent to Registry * Reformat using black * Switched from pkl state file to json * print() has a flush argument * Trivial changes * If mepo1 style state, print warning to run 'mepo update-state'. Updated update-state code. * Removing hashes from requirements.txt * Fixed bug in string formatting * Update comment in registry.py * Store full path to the fixture dir in state file * Revert "Store full path to the fixture dir in state file". We want relocability. This reverts commit 1bc83308f8de54ab5840d8554e3a9840c0e9f893. * Local path saved to state file is relative to fixture directory --- .github/workflows/run-formatter.yaml | 23 + .github/workflows/run-linter.yaml | 23 + .../workflows/{mepo.yaml => run-tests.yaml} | 8 +- CHANGELOG.md | 12 +- README.md | 2 +- bin/mepo | 6 +- docs/make_md_docs.py | 82 +-- mepo.spec | 51 ++ poetry.lock | 313 +++++++++ pyproject.toml | 38 +- requirements.txt | 18 +- src/mepo/__init__.py | 0 src/mepo/__main__.py | 19 + src/mepo/cmdline/__init__.py | 0 src/mepo/cmdline/branch_parser.py | 70 +- src/mepo/cmdline/config_parser.py | 62 +- src/mepo/cmdline/parser.py | 626 +++++++++--------- src/mepo/cmdline/stash_parser.py | 78 ++- src/mepo/cmdline/tag_parser.py | 102 ++- src/mepo/command/__init__.py | 0 src/mepo/command/branch.py | 12 + src/mepo/command/branch/branch.py | 15 - .../create/create.py => branch_create.py} | 9 +- .../delete/delete.py => branch_delete.py} | 9 +- .../{tag/list/list.py => branch_list.py} | 14 +- .../{changed-files => }/changed-files.py | 29 +- src/mepo/command/checkout-if-exists.py | 34 + .../checkout-if-exists/checkout-if-exists.py | 25 - src/mepo/command/checkout.py | 41 ++ src/mepo/command/checkout/checkout.py | 32 - src/mepo/command/{clone => }/clone.py | 99 +-- src/mepo/command/command.py | 11 - src/mepo/command/{commit => }/commit.py | 37 +- src/mepo/command/{compare => }/compare.py | 78 ++- src/mepo/command/config.py | 14 + src/mepo/command/config/config.py | 17 - src/mepo/command/config/get/get.py | 13 - .../delete/delete.py => config_delete.py} | 3 +- src/mepo/command/config_get.py | 18 + .../print/print.py => config_print.py} | 3 +- .../{config/set/set.py => config_set.py} | 3 +- src/mepo/command/{develop => }/develop.py | 19 +- src/mepo/command/{diff => }/diff.py | 27 +- src/mepo/command/{fetch => }/fetch.py | 13 +- src/mepo/command/init.py | 25 + src/mepo/command/init/init.py | 22 - src/mepo/command/list.py | 9 + src/mepo/command/list/list.py | 7 - src/mepo/command/pull-all.py | 30 + src/mepo/command/pull-all/pull-all.py | 22 - src/mepo/command/pull.py | 27 + src/mepo/command/pull/pull.py | 21 - src/mepo/command/{push => }/push.py | 9 +- src/mepo/command/{reset => }/reset.py | 58 +- src/mepo/command/restore-state.py | 41 ++ .../command/restore-state/restore-state.py | 31 - src/mepo/command/{save => }/save.py | 56 +- src/mepo/command/{stage => }/stage.py | 13 +- src/mepo/command/stash.py | 16 + src/mepo/command/stash/stash.py | 19 - .../{stash/apply/apply.py => stash_apply.py} | 9 +- .../{stash/list/list.py => stash_list.py} | 11 +- .../{stash/pop/pop.py => stash_pop.py} | 9 +- .../{stash/push/push.py => stash_push.py} | 9 +- .../{stash/show/show.py => stash_show.py} | 7 +- src/mepo/command/{status => }/status.py | 56 +- src/mepo/command/tag.py | 14 + src/mepo/command/tag/tag.py | 17 - .../{tag/create/create.py => tag_create.py} | 32 +- .../{tag/delete/delete.py => tag_delete.py} | 10 +- .../{branch/list/list.py => tag_list.py} | 14 +- .../command/{tag/push/push.py => tag_push.py} | 14 +- src/mepo/command/{unstage => }/unstage.py | 12 +- src/mepo/command/update-state.py | 23 + src/mepo/command/{whereis => }/whereis.py | 29 +- src/mepo/component.py | 247 +++++++ src/mepo/config/config_file.py | 55 -- src/mepo/git.py | 579 ++++++++++++++++ src/mepo/main.py | 6 - src/mepo/registry.py | 80 +++ src/mepo/repository/git.py | 421 ------------ src/mepo/state.py | 168 +++++ src/mepo/state/component.py | 216 ------ src/mepo/state/state.py | 126 ---- src/mepo/utilities/__init__.py | 0 src/mepo/utilities/chdir.py | 14 + src/mepo/utilities/colors.py | 20 +- src/mepo/{state => utilities}/exceptions.py | 17 +- src/mepo/utilities/mepoconfig.py | 30 +- src/mepo/utilities/shellcmd.py | 7 +- src/mepo/utilities/verify.py | 7 +- src/mepo/utilities/version.py | 42 +- tests/input/args.py | 2 - tests/output/compare_brief_output.txt | 5 - tests/output/compare_full_output.txt | 11 - tests/output/list_output.txt | 1 - tests/output/output_branch_create.txt | 1 + tests/output/output_branch_delete.txt | 1 + tests/output/output_branch_list.txt | 11 + tests/output/output_clone_status.txt | 11 + tests/output/output_compare.txt | 3 + tests/output/output_compare_all.txt | 12 + ...s_output.txt => output_develop_status.txt} | 9 +- tests/output/output_diff.txt | 5 + tests/output/output_list.txt | 1 + tests/output/output_pull_all.txt | 1 + tests/output/output_push.txt | 7 + tests/output/output_reset.txt | 10 + tests/output/output_tag_create.txt | 2 + tests/output/output_tag_delete.txt | 2 + tests/output/output_whereis.txt | 10 + tests/test_mepo_commands.py | 366 ++++++++-- 112 files changed, 3249 insertions(+), 2037 deletions(-) create mode 100644 .github/workflows/run-formatter.yaml create mode 100644 .github/workflows/run-linter.yaml rename .github/workflows/{mepo.yaml => run-tests.yaml} (82%) create mode 100644 mepo.spec create mode 100644 poetry.lock create mode 100644 src/mepo/__init__.py create mode 100644 src/mepo/__main__.py create mode 100644 src/mepo/cmdline/__init__.py create mode 100644 src/mepo/command/__init__.py create mode 100644 src/mepo/command/branch.py delete mode 100644 src/mepo/command/branch/branch.py rename src/mepo/command/{branch/create/create.py => branch_create.py} (62%) rename src/mepo/command/{branch/delete/delete.py => branch_delete.py} (63%) rename src/mepo/command/{tag/list/list.py => branch_list.py} (68%) rename src/mepo/command/{changed-files => }/changed-files.py (65%) create mode 100644 src/mepo/command/checkout-if-exists.py delete mode 100644 src/mepo/command/checkout-if-exists/checkout-if-exists.py create mode 100644 src/mepo/command/checkout.py delete mode 100644 src/mepo/command/checkout/checkout.py rename src/mepo/command/{clone => }/clone.py (53%) delete mode 100644 src/mepo/command/command.py rename src/mepo/command/{commit => }/commit.py (54%) rename src/mepo/command/{compare => }/compare.py (52%) create mode 100644 src/mepo/command/config.py delete mode 100644 src/mepo/command/config/config.py delete mode 100644 src/mepo/command/config/get/get.py rename src/mepo/command/{config/delete/delete.py => config_delete.py} (78%) create mode 100644 src/mepo/command/config_get.py rename src/mepo/command/{config/print/print.py => config_print.py} (50%) rename src/mepo/command/{config/set/set.py => config_set.py} (81%) rename src/mepo/command/{develop => }/develop.py (54%) rename src/mepo/command/{diff => }/diff.py (75%) rename src/mepo/command/{fetch => }/fetch.py (66%) create mode 100644 src/mepo/command/init.py delete mode 100644 src/mepo/command/init/init.py create mode 100644 src/mepo/command/list.py delete mode 100644 src/mepo/command/list/list.py create mode 100644 src/mepo/command/pull-all.py delete mode 100644 src/mepo/command/pull-all/pull-all.py create mode 100644 src/mepo/command/pull.py delete mode 100644 src/mepo/command/pull/pull.py rename src/mepo/command/{push => }/push.py (63%) rename src/mepo/command/{reset => }/reset.py (61%) create mode 100644 src/mepo/command/restore-state.py delete mode 100644 src/mepo/command/restore-state/restore-state.py rename src/mepo/command/{save => }/save.py (50%) rename src/mepo/command/{stage => }/stage.py (78%) create mode 100644 src/mepo/command/stash.py delete mode 100644 src/mepo/command/stash/stash.py rename src/mepo/command/{stash/apply/apply.py => stash_apply.py} (63%) rename src/mepo/command/{stash/list/list.py => stash_list.py} (53%) rename src/mepo/command/{stash/pop/pop.py => stash_pop.py} (63%) rename src/mepo/command/{stash/push/push.py => stash_push.py} (64%) rename src/mepo/command/{stash/show/show.py => stash_show.py} (73%) rename src/mepo/command/{status => }/status.py (66%) create mode 100644 src/mepo/command/tag.py delete mode 100644 src/mepo/command/tag/tag.py rename src/mepo/command/{tag/create/create.py => tag_create.py} (60%) rename src/mepo/command/{tag/delete/delete.py => tag_delete.py} (75%) rename src/mepo/command/{branch/list/list.py => tag_list.py} (68%) rename src/mepo/command/{tag/push/push.py => tag_push.py} (64%) rename src/mepo/command/{unstage => }/unstage.py (73%) create mode 100644 src/mepo/command/update-state.py rename src/mepo/command/{whereis => }/whereis.py (60%) create mode 100644 src/mepo/component.py delete mode 100644 src/mepo/config/config_file.py create mode 100644 src/mepo/git.py delete mode 100644 src/mepo/main.py create mode 100644 src/mepo/registry.py delete mode 100644 src/mepo/repository/git.py create mode 100644 src/mepo/state.py delete mode 100644 src/mepo/state/component.py delete mode 100644 src/mepo/state/state.py create mode 100644 src/mepo/utilities/__init__.py create mode 100644 src/mepo/utilities/chdir.py rename src/mepo/{state => utilities}/exceptions.py (75%) delete mode 100644 tests/input/args.py delete mode 100644 tests/output/compare_brief_output.txt delete mode 100644 tests/output/compare_full_output.txt delete mode 100644 tests/output/list_output.txt create mode 100644 tests/output/output_branch_create.txt create mode 100644 tests/output/output_branch_delete.txt create mode 100644 tests/output/output_branch_list.txt create mode 100644 tests/output/output_clone_status.txt create mode 100644 tests/output/output_compare.txt create mode 100644 tests/output/output_compare_all.txt rename tests/output/{status_output.txt => output_develop_status.txt} (53%) create mode 100644 tests/output/output_diff.txt create mode 100644 tests/output/output_list.txt create mode 100644 tests/output/output_pull_all.txt create mode 100644 tests/output/output_push.txt create mode 100644 tests/output/output_reset.txt create mode 100644 tests/output/output_tag_create.txt create mode 100644 tests/output/output_tag_delete.txt create mode 100644 tests/output/output_whereis.txt diff --git a/.github/workflows/run-formatter.yaml b/.github/workflows/run-formatter.yaml new file mode 100644 index 0000000..3f0351a --- /dev/null +++ b/.github/workflows/run-formatter.yaml @@ -0,0 +1,23 @@ +name: Run formatter + +on: [push] + +jobs: + format: + runs-on: ubuntu-latest + name: Format code + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.9 + uses: actions/setup-python@v5 + with: + python-version: 3.9 + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + timeout-minutes: 5 + - name: Run black + run: black --check . + timeout-minutes: 5 diff --git a/.github/workflows/run-linter.yaml b/.github/workflows/run-linter.yaml new file mode 100644 index 0000000..b4fb21c --- /dev/null +++ b/.github/workflows/run-linter.yaml @@ -0,0 +1,23 @@ +name: Run linter + +on: [push] + +jobs: + lint: + runs-on: ubuntu-latest + name: Lint code + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.9 + uses: actions/setup-python@v5 + with: + python-version: 3.9 + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + timeout-minutes: 5 + - name: Run pylint + run: pylint --exit-zero src/mepo + timeout-minutes: 5 diff --git a/.github/workflows/mepo.yaml b/.github/workflows/run-tests.yaml similarity index 82% rename from .github/workflows/mepo.yaml rename to .github/workflows/run-tests.yaml index 50206b9..87c4cd5 100644 --- a/.github/workflows/mepo.yaml +++ b/.github/workflows/run-tests.yaml @@ -1,4 +1,4 @@ -name: Unit testing of mepo +name: Run tests on: [push] @@ -26,6 +26,8 @@ jobs: pip install -r requirements.txt timeout-minutes: 5 - - name: Run unit tests - run: python3 tests/test_mepo_commands.py -v + - name: Run tests + run: | + export PYTHONPATH=$(pwd)/src:$PYTHONPATH + python tests/test_mepo_commands.py -v timeout-minutes: 5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 844940d..2d73ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Converted `mepo` to a Python project via the following renaming - -- `mepo.d` -> `src/mepo` - -- `mepo.d/utest` -> `tests` - -- `doc` --> `docs` + -- Added `src/mepo/__init__.py` + -- Renamed `mepo.d` -> `src/mepo` + -- Renamed `mepo.d/utest` -> `tests` + -- Renamed `doc` --> `docs` + -- A `mepo` config file is now called a `mepo` registry + -- More code reorganization + - Helper script `mepo`, used for development, moved to the `bin` directory. +- Expanded testing to cover more `mepo` commands + ## [1.52.0] - 2024-01-10 ### Added diff --git a/README.md b/README.md index 2a9b289..bdc8585 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # mepo [![Actions Status](https://github.com/pchakraborty/mepo/workflows/Unit%20testing%20of%20mepo/badge.svg)](https://github.com/pchakraborty/mepo/actions) [![DOI](https://zenodo.org/badge/215067850.svg)](https://zenodo.org/badge/latestdoi/215067850) -`mepo` is a tool, written in Python3 (3.6.0+), to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. +`mepo` is a tool, written in Python3 (3.9.0+), to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. ## Commands diff --git a/bin/mepo b/bin/mepo index fb10b0b..225acac 100755 --- a/bin/mepo +++ b/bin/mepo @@ -8,10 +8,10 @@ import traceback if sys.version_info < (3, 9, 0): sys.exit('ERROR: Python version needs to be >= 3.9.0') -# Add direactory containing mepo to path -SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src') +# Add directory containing mepo to path +SRC_D = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "src") sys.path.insert(0, SRC_D) if __name__ == '__main__': - from mepo.main import main + from mepo.__main__ import main main() diff --git a/docs/make_md_docs.py b/docs/make_md_docs.py index 07ba6d0..7fbab10 100644 --- a/docs/make_md_docs.py +++ b/docs/make_md_docs.py @@ -2,60 +2,52 @@ import os import io +import glob from mdutils.mdutils import MdUtils import subprocess as sp -preamble=''' +preamble = """ mepo provides many different commands for working with a multi-repository fixture. -''' +""" # Assume this script is in mepo/doc. Then we need to get to the mepo/mepo.d/command directory doc_dir_path = os.path.dirname(os.path.realpath(__file__)) # Then we need to get to the mepo/mepo.d/command directory. First the "main" dir main_dir_path = os.path.dirname(doc_dir_path) -# Now add 'src/mepo' -mepod_dir_path = os.path.join(main_dir_path, 'src', 'mepo') -# And then 'command' -command_dir_path = os.path.join(mepod_dir_path, 'command') +# Now add "src/mepo" +mepod_dir_path = os.path.join(main_dir_path, "src", "mepo") +# And then "command" +command_dir_path = os.path.join(mepod_dir_path, "command") -mepo_command_path = os.path.join(main_dir_path, 'bin', 'mepo') +mepo_command_path = os.path.join(main_dir_path, "bin", "mepo") -def get_command_list(directory): - # Walk the tree - roots = [x[0] for x in os.walk(directory)] - - # Now remove "." from the list - roots = roots[1:] - - # Just get the relative paths - rel_roots = [os.path.relpath(x,directory) for x in roots] - - # Now exclude __pycache__ - command_dirs = [x for x in rel_roots if '__pycache__' not in x] - # Convert slashes to spaces - all_commands = [x.replace('/',' ') for x in command_dirs] +def get_command_list(directory): + # Get all commands + all_commands_py = glob.glob(os.path.join(directory, "*.py")) + all_commands = [os.path.basename(x).replace(".py", "") for x in all_commands_py] + all_commands.remove("command") # manually remove # Now let's find the commands that have subcommands - ## First we get commands with spaces - commands_with_spaces = [x for x in all_commands if ' ' in x] - ## Now let's just get the first elements - temp = [x.split()[0] for x in commands_with_spaces] - ## Get the uniques - commands_with_subcommands = list(set(temp)) - - # Now remove those from our list - all_useful_commands = [x for x in all_commands if x not in commands_with_subcommands] + ## First we get commands with underscore + ## Then replace underscore with a space + commands_with_underscore = [x for x in all_commands if "_" in x] + commands_with_subcommands = [x.replace("_", " ") for x in commands_with_underscore] + all_useful_commands = [x for x in all_commands if x not in commands_with_underscore] + all_useful_commands += commands_with_subcommands return sorted(all_useful_commands) + def create_markdown_from_usage(command, mdFile): - cmd = [mepo_command_path, command, '--help'] + cmd = [mepo_command_path, command, "--help"] # Some commands have spaces, so we need to break it up again - cmd = ' '.join(cmd).split() + cmd = " ".join(cmd).split() - result = sp.run(cmd, capture_output=True, universal_newlines=True, env={'COLUMNS':'256'}) + result = sp.run( + cmd, capture_output=True, universal_newlines=True, env={"COLUMNS": "256"} + ) output = result.stdout output_list = output.split("\n") @@ -66,25 +58,32 @@ def create_markdown_from_usage(command, mdFile): # Usage usage = output_list[0] - usage = usage.replace('usage: ','') + usage = usage.replace("usage: ", "") mdFile.new_header(level=3, title="Usage") mdFile.insert_code(usage) - positional_arguments = output.partition('positional arguments:\n')[2].partition('\n\n')[0] + positional_arguments = output.partition("positional arguments:\n")[2].partition( + "\n\n" + )[0] if positional_arguments: mdFile.new_header(level=3, title="Positional Arguments") mdFile.insert_code(positional_arguments) - optional_arguments = output.partition('optional arguments:\n')[2].partition('\n\n')[0] + optional_arguments = output.partition("optional arguments:\n")[2].partition("\n\n")[ + 0 + ] # Remove extra blank lines - optional_arguments = os.linesep.join([s for s in optional_arguments.splitlines() if s]) + optional_arguments = os.linesep.join( + [s for s in optional_arguments.splitlines() if s] + ) if optional_arguments: mdFile.new_header(level=3, title="Optional Arguments") mdFile.insert_code(optional_arguments) + if __name__ == "__main__": - doc_file = 'Mepo-Commands.md' + doc_file = "Mepo-Commands.md" mdFile = MdUtils(file_name=doc_file) mdFile.new_header(level=1, title="Overview") @@ -94,8 +93,9 @@ def create_markdown_from_usage(command, mdFile): command_list = get_command_list(command_dir_path) for command in command_list: mdFile.new_header(level=2, title=command) - create_markdown_from_usage(command,mdFile) + print(f"mepo command: {command}") + create_markdown_from_usage(command, mdFile) - mdFile.new_table_of_contents(table_title='Table of Contents', depth=2) + mdFile.new_table_of_contents(table_title="Table of Contents", depth=2) mdFile.create_md_file() - print(f'Generated {doc_file}.') + print(f"Generated {doc_file}.") diff --git a/mepo.spec b/mepo.spec new file mode 100644 index 0000000..ef59192 --- /dev/null +++ b/mepo.spec @@ -0,0 +1,51 @@ +# -*- mode: python ; coding: utf-8 -*- + +import os +import glob + +cmd_dir = os.path.join(SPECPATH, 'src/mepo/command') +cmd_list = [os.path.basename(x).split('.')[0] for x in glob.glob(os.path.join(cmd_dir, '*.py'))] +hidden_imports = [f'mepo.command.{x}' for x in cmd_list if '_' not in x] # exclude subcommands +print(f'hidden_imports: {hidden_imports}') + +a = Analysis( + ['src/mepo/__main__.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=hidden_imports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='mepo', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='mepo', +) diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..e6a0607 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,313 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdutils" +version = "1.6.0" +description = "Useful package for creating Markdown files while executing python code." +optional = false +python-versions = "^3.6" +files = [ + {file = "mdutils-1.6.0.tar.gz", hash = "sha256:647f3cf00df39fee6c57fa6738dc1160fce1788276b5530c87d43a70cdefdaf1"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "6d1408dadf47789115389ea091cb4fc463e03cce68426149f680c3702272342e" diff --git a/pyproject.toml b/pyproject.toml index 0379e92..dd994ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,26 +1,22 @@ -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - -[project] +[tool.poetry] name = "mepo" -version = "v2.0.0" -authors = [ - {name = "GMAO SI Team", email = "siteam@gmao.gsfc.nasa.gov"}, -] -description = "Tool to manage (m)ultiple r(epo)sitories" -requires-python = ">= 3.9" -license = {file = "LICENSE"} +version = "2.0.0" +description = "A tool for managing (m)ultiple r(epo)s" +authors = ["GMAO SI Team "] +license = "Apache-2.0" readme = "README.md" -dependencies = [ - "pyyaml>=6.0.1", -] +repository = "https://github.com/GEOS-ESM/mepo.git" -[project.urls] -Homepage = "https://github.com/GEOS-ESM/mepo" +[tool.poetry.dependencies] +python = "^3.9" +PyYAML = "^6.0.1" +black = "^24.4.2" +mdutils = "^1.6.0" +pylint = "^3.1.0" -[project.scripts] -mepo = "mepo.main:main" +[tool.poetry.scripts] +mepo = "mepo.__main__:main" -[tool.setuptools] -include-package-data = true +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt index c0822dc..873be6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,17 @@ -PyYAML>=5.4 +astroid==3.1.0 ; python_version >= "3.9" and python_version < "4.0" +black==24.4.2 ; python_version >= "3.9" and python_version < "4.0" +click==8.1.7 ; python_version >= "3.9" and python_version < "4.0" +colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") +dill==0.3.8 ; python_version >= "3.9" and python_version < "4.0" +isort==5.13.2 ; python_version >= "3.9" and python_version < "4.0" +mccabe==0.7.0 ; python_version >= "3.9" and python_version < "4.0" +mdutils==1.6.0 ; python_version >= "3.9" and python_version < "4.0" +mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "4.0" +packaging==24.0 ; python_version >= "3.9" and python_version < "4.0" +pathspec==0.12.1 ; python_version >= "3.9" and python_version < "4.0" +platformdirs==4.2.1 ; python_version >= "3.9" and python_version < "4.0" +pylint==3.1.0 ; python_version >= "3.9" and python_version < "4.0" +pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0" +tomli==2.0.1 ; python_version >= "3.9" and python_version < "3.11" +tomlkit==0.12.4 ; python_version >= "3.9" and python_version < "4.0" +typing-extensions==4.11.0 ; python_version >= "3.9" and python_version < "3.11" diff --git a/src/mepo/__init__.py b/src/mepo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mepo/__main__.py b/src/mepo/__main__.py new file mode 100644 index 0000000..3da1557 --- /dev/null +++ b/src/mepo/__main__.py @@ -0,0 +1,19 @@ +from importlib import import_module + +from mepo.cmdline.parser import MepoArgParser +from mepo.utilities import mepoconfig + + +def main(): + args = MepoArgParser().parse() + mepo_cmd = mepoconfig.get_alias_command(args.mepo_cmd) + + # Load the module containing the "run" method of specified command + cmd_module = import_module(f"mepo.command.{mepo_cmd}") + + # Execute "run" method of specified command + cmd_module.run(args) + + +if __name__ == "__main__": + main() diff --git a/src/mepo/cmdline/__init__.py b/src/mepo/cmdline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mepo/cmdline/branch_parser.py b/src/mepo/cmdline/branch_parser.py index 57d2347..db0cad5 100644 --- a/src/mepo/cmdline/branch_parser.py +++ b/src/mepo/cmdline/branch_parser.py @@ -1,11 +1,12 @@ import argparse + class MepoBranchArgParser(object): def __init__(self, branch): self.branch_subparsers = branch.add_subparsers() - self.branch_subparsers.title = 'mepo branch sub-commands' - self.branch_subparsers.dest = 'mepo_branch_cmd' + self.branch_subparsers.title = "mepo branch sub-commands" + self.branch_subparsers.dest = "mepo_branch_cmd" self.branch_subparsers.required = True self.__list() self.__create() @@ -13,46 +14,47 @@ def __init__(self, branch): def __list(self): brlist = self.branch_subparsers.add_parser( - 'list', - description = 'List local branches. If no component is specified, runs over all components') + "list", + description="List local branches. If no component is specified, runs over all components", + ) + brlist.add_argument( + "-a", "--all", action="store_true", help="list all (local+remote) branches" + ) brlist.add_argument( - '-a', '--all', - action = 'store_true', - help = 'list all (local+remote) branches') + "--nocolor", action="store_true", help="do not display color" + ) brlist.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to list branches in') + "comp_name", + metavar="comp-name", + nargs="*", + help="component to list branches in", + ) def __create(self): create = self.branch_subparsers.add_parser( - 'create', - description = 'Create branch in component ') + "create", description="Create branch in component " + ) + create.add_argument("branch_name", metavar="branch-name", help="name of branch") create.add_argument( - 'branch_name', - metavar = 'branch-name', - help = "Name of branch") - create.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to create branches in') + "comp_name", + metavar="comp-name", + nargs="+", + help="component to create branches in", + ) def __delete(self): delete = self.branch_subparsers.add_parser( - 'delete', - description = 'Delete branch in component ') - delete.add_argument( - 'branch_name', - metavar = 'branch-name', - help = "Name of branch") + "delete", description="Delete branch in component " + ) + delete.add_argument("branch_name", metavar="branch-name", help="name of branch") delete.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to delete branches in') + "comp_name", + metavar="comp-name", + nargs="+", + help="component to delete branches in", + ) delete.add_argument( - '--force', - action = 'store_true', - help = 'Delete branch even if it has not been fully merged') + "--force", + action="store_true", + help="delete branch even if it has not been fully merged", + ) diff --git a/src/mepo/cmdline/config_parser.py b/src/mepo/cmdline/config_parser.py index 46cd1c8..6516076 100644 --- a/src/mepo/cmdline/config_parser.py +++ b/src/mepo/cmdline/config_parser.py @@ -1,12 +1,13 @@ import argparse import textwrap + class MepoConfigArgParser(object): def __init__(self, config): self.config = config.add_subparsers() - self.config.title = 'mepo config sub-commands' - self.config.dest = 'mepo_config_cmd' + self.config.title = "mepo config sub-commands" + self.config.dest = "mepo_config_cmd" self.config.required = True self.__get() self.__set() @@ -15,42 +16,39 @@ def __init__(self, config): def __get(self): get = self.config.add_parser( - 'get', - description = ('Get config `entry` in `.mepoconfig`. ' - 'Note this uses gitconfig style where `entry` is of the form `section.option`. ' - 'So to get an `alias` `st` You would run `mepo config get alias.st`')) - get.add_argument( - 'entry', - metavar = 'entry', - help = 'Entry to display.') + "get", + description=( + "Get config `entry` in `.mepoconfig`. " + "Note this uses gitconfig style where `entry` is of the form `section.option`. " + "So to get an `alias` `st` You would run `mepo config get alias.st`" + ), + ) + get.add_argument("entry", metavar="entry", help="Entry to display.") def __set(self): set = self.config.add_parser( - 'set', - description = ('Set config `entry` to `value` in `.mepoconfig`. ' - 'Note this uses gitconfig style where `entry` is of the form `section.option`. ' - 'So to set an `alias` for `status` of `st` You would run `mepo config set alias.st status`')) - set.add_argument( - 'entry', - metavar = 'entry', - help = 'Entry to set.') - set.add_argument( - 'value', - metavar = 'value', - help = 'Value to set entry to.') + "set", + description=( + "Set config `entry` to `value` in `.mepoconfig`. " + "Note this uses gitconfig style where `entry` is of the form `section.option`. " + "So to set an `alias` for `status` of `st` You would run `mepo config set alias.st status`" + ), + ) + set.add_argument("entry", metavar="entry", help="Entry to set.") + set.add_argument("value", metavar="value", help="Value to set entry to.") def __delete(self): delete = self.config.add_parser( - 'delete', - description = ('Delete config `entry` in `.mepoconfig`. ' - 'Note this uses gitconfig style where `entry` is of the form `section.option`. ' - 'So to delete an `alias` `st` You would run `mepo config delete alias.st`')) - delete.add_argument( - 'entry', - metavar = 'entry', - help = 'Entry to delete.') + "delete", + description=( + "Delete config `entry` in `.mepoconfig`. " + "Note this uses gitconfig style where `entry` is of the form `section.option`. " + "So to delete an `alias` `st` You would run `mepo config delete alias.st`" + ), + ) + delete.add_argument("entry", metavar="entry", help="Entry to delete.") def __print(self): print = self.config.add_parser( - 'print', - description = 'Print contents of `.mepoconfig`') + "print", description="Print contents of `.mepoconfig`" + ) diff --git a/src/mepo/cmdline/parser.py b/src/mepo/cmdline/parser.py index 237cc04..295b76e 100644 --- a/src/mepo/cmdline/parser.py +++ b/src/mepo/cmdline/parser.py @@ -1,22 +1,25 @@ import argparse -from mepo.cmdline.branch_parser import MepoBranchArgParser -from mepo.cmdline.stash_parser import MepoStashArgParser -from mepo.cmdline.tag_parser import MepoTagArgParser -from mepo.cmdline.config_parser import MepoConfigArgParser -from mepo.utilities import mepoconfig +from .branch_parser import MepoBranchArgParser +from .stash_parser import MepoStashArgParser +from .tag_parser import MepoTagArgParser +from .config_parser import MepoConfigArgParser + +from ..utilities import mepoconfig + class MepoArgParser(object): - __slots__ = ['parser', 'subparsers'] + __slots__ = ["parser", "subparsers"] def __init__(self): self.parser = argparse.ArgumentParser( - description = 'Tool to manage (m)ultiple r(epo)s') + description="Tool to manage (m)ultiple r(epo)s" + ) self.subparsers = self.parser.add_subparsers() - self.subparsers.title = 'mepo commands' + self.subparsers.title = "mepo commands" self.subparsers.required = True - self.subparsers.dest = 'mepo_cmd' + self.subparsers.dest = "mepo_cmd" def parse(self): self.__init() @@ -44,424 +47,451 @@ def parse(self): self.__push() self.__save() self.__config() + self.__update_state() return self.parser.parse_args() def __init(self): init = self.subparsers.add_parser( - 'init', - description = 'Initialize mepo based on `config-file`', - aliases=mepoconfig.get_command_alias('init')) + "init", + description="Initialize mepo based on `config-file`", + aliases=mepoconfig.get_command_alias("init"), + ) init.add_argument( - '--config', - metavar = 'config-file', - nargs = '?', - default = 'components.yaml', - help = 'default: %(default)s') + "--registry", + nargs="?", + default="components.yaml", + help="default: %(default)s", + ) init.add_argument( - '--style', - metavar = 'style-type', - nargs = '?', - default = None, - choices = ['naked', 'prefix','postfix'], - help = 'Style of directory file, default: prefix, allowed options: %(choices)s') + "--style", + metavar="style-type", + nargs="?", + default=None, + choices=["naked", "prefix", "postfix"], + help="Style of directory file, default: prefix, allowed options: %(choices)s", + ) def __clone(self): clone = self.subparsers.add_parser( - 'clone', - description = "Clone repositories.", - aliases=mepoconfig.get_command_alias('clone')) + "clone", + description="Clone repositories.", + aliases=mepoconfig.get_command_alias("clone"), + ) clone.add_argument( - 'repo_url', - metavar = 'URL', - nargs = '?', - default = None, - help = 'URL to clone') + "repo_url", metavar="URL", nargs="?", default=None, help="URL to clone" + ) clone.add_argument( - 'directory', - nargs = '?', - default = None, - help = "Directory to clone into (Only allowed with URL!)") + "directory", + nargs="?", + default=None, + help="Directory to clone into (Only allowed with URL!)", + ) clone.add_argument( - '--branch','-b', - metavar = 'name', - nargs = '?', - default = None, - help = 'Branch/tag of URL to initially clone (Only allowed with URL!)') + "--branch", + "-b", + metavar="name", + nargs="?", + default=None, + help="Branch/tag of URL to initially clone (Only allowed with URL!)", + ) clone.add_argument( - '--config', - metavar = 'config-file', - nargs = '?', - default = None, - help = 'Configuration file (ignored if init already called)') + "--registry", + metavar="registry", + nargs="?", + default=None, + help="Registry (default: components.yaml)", + ) clone.add_argument( - '--style', - metavar = 'style-type', - nargs = '?', - default = None, - choices = ['naked', 'prefix','postfix'], - help = 'Style of directory file, default: prefix, allowed options: %(choices)s (ignored if init already called)') + "--style", + metavar="style-type", + nargs="?", + default=None, + choices=["naked", "prefix", "postfix"], + help="Style of directory file, default: prefix, allowed options: %(choices)s (ignored if init already called)", + ) clone.add_argument( - '--allrepos', - action = 'store_true', - help = 'Must be passed with -b/--branch. When set, it not only checkouts out the branch/tag for the fixture, but for all the subrepositories as well.') + "--allrepos", + action="store_true", + help="Must be passed with -b/--branch. When set, it not only checkouts out the branch/tag for the fixture, but for all the subrepositories as well.", + ) clone.add_argument( - '--partial', - metavar = 'partial-type', - nargs = '?', - default = None, - choices = ['off','blobless','treeless'], - help = 'Style of partial clone, default: None, allowed options: %(choices)s. Off means a "normal" full git clone, blobless means cloning with "--filter=blob:none" and treeless means cloning with "--filter=tree:0". NOTE: We do *not* recommend using "treeless" as it is very aggressive and will cause problems with many git commands.') + "--partial", + metavar="partial-type", + nargs="?", + default=None, + choices=["off", "blobless", "treeless"], + help='Style of partial clone, default: None, allowed options: %(choices)s. Off means a "normal" full git clone, blobless means cloning with "--filter=blob:none" and treeless means cloning with "--filter=tree:0". NOTE: We do *not* recommend using "treeless" as it is very aggressive and will cause problems with many git commands.', + ) def __list(self): listcomps = self.subparsers.add_parser( - 'list', - description = 'List all components that are being tracked', - aliases=mepoconfig.get_command_alias('list')) + "list", + description="List all components that are being tracked", + aliases=mepoconfig.get_command_alias("list"), + ) + listcomps.add_argument( + "-1", "--one-per-line", action="store_true", help="one component per line" + ) def __status(self): status = self.subparsers.add_parser( - 'status', - description = 'Check current status of all components', - aliases=mepoconfig.get_command_alias('status')) + "status", + description="Check current status of all components", + aliases=mepoconfig.get_command_alias("status"), + ) status.add_argument( - '--ignore-permissions', - action = 'store_true', - help = 'Tells command to ignore changes in file permissions.') + "--ignore-permissions", + action="store_true", + help="Tells command to ignore changes in file permissions.", + ) status.add_argument( - '--nocolor', - action = 'store_true', - help = 'Tells status to not display colors.') + "--nocolor", action="store_true", help="Tells status to not display colors." + ) status.add_argument( - '--hashes', - action = 'store_true', - help = 'Print the exact hash of the HEAD.') + "--hashes", action="store_true", help="Print the exact hash of the HEAD." + ) def __restore_state(self): restore_state = self.subparsers.add_parser( - 'restore-state', - description = 'Restores all components to the last saved state.', - aliases=mepoconfig.get_command_alias('restore-state')) + "restore-state", + description="Restores all components to the last saved state.", + aliases=mepoconfig.get_command_alias("restore-state"), + ) def __diff(self): diff = self.subparsers.add_parser( - 'diff', - description = 'Diff all components', - aliases=mepoconfig.get_command_alias('diff')) + "diff", + description="Diff all components", + aliases=mepoconfig.get_command_alias("diff"), + ) diff.add_argument( - '--name-only', - action = 'store_true', - help = 'Show only names of changed files') + "--name-only", action="store_true", help="Show only names of changed files" + ) diff.add_argument( - '--name-status', - action = 'store_true', - help = 'Show name-status of changed files') + "--name-status", + action="store_true", + help="Show name-status of changed files", + ) diff.add_argument( - '--ignore-permissions', - action = 'store_true', - help = 'Tells command to ignore changes in file permissions.') + "--ignore-permissions", + action="store_true", + help="Tells command to ignore changes in file permissions.", + ) diff.add_argument( - '--staged', - action = 'store_true', - help = 'Show diff of staged changes') + "--staged", action="store_true", help="Show diff of staged changes" + ) diff.add_argument( - '-b','--ignore-space-change', - action = 'store_true', - help = 'Ignore changes in amount of whitespace') + "-b", + "--ignore-space-change", + action="store_true", + help="Ignore changes in amount of whitespace", + ) diff.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to list branches in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to list branches in", + ) def __checkout(self): checkout = self.subparsers.add_parser( - 'checkout', - description = "Switch to branch/tag `branch-name` in component `comp-name`. " + "checkout", + description="Switch to branch/tag `branch-name` in component `comp-name`. " "If no components listed, checkout from all. " "Specifying `-b` causes the branch `branch-name` to be created and checked out.", - aliases=mepoconfig.get_command_alias('checkout')) + aliases=mepoconfig.get_command_alias("checkout"), + ) checkout.add_argument( - 'branch_name', - metavar = 'branch-name', - help = "Name of branch") + "branch_name", metavar="branch-name", help="Name of branch" + ) checkout.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Components to checkout branch in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Components to checkout branch in", + ) + checkout.add_argument("-b", action="store_true", help="create the branch") checkout.add_argument( - '-b', - action = 'store_true', - help = 'create the branch') + "-q", "--quiet", action="store_true", help="Suppress prints" + ) checkout.add_argument( - '-q', '--quiet', - action = 'store_true', - help = 'Suppress prints') - checkout.add_argument( - '--detach', - action = 'store_true', - help = 'Detach upon checkout') + "--detach", action="store_true", help="Detach upon checkout" + ) def __checkout_if_exists(self): checkout_if_exists = self.subparsers.add_parser( - 'checkout-if-exists', - description = 'Switch to branch or tag `ref-name` in any component where it is present. ', - aliases=mepoconfig.get_command_alias('checkout-if-exists')) + "checkout-if-exists", + description="Switch to branch or tag `ref-name` in any component where it is present. ", + aliases=mepoconfig.get_command_alias("checkout-if-exists"), + ) checkout_if_exists.add_argument( - 'ref_name', - metavar = 'ref-name', - help = "Name of branch or tag") + "ref_name", metavar="ref-name", help="Name of branch or tag" + ) checkout_if_exists.add_argument( - '-q', '--quiet', - action = 'store_true', - help = 'Suppress prints') + "-q", "--quiet", action="store_true", help="Suppress prints" + ) checkout_if_exists.add_argument( - '--detach', - action = 'store_true', - help = 'Detach on checkout') + "--detach", action="store_true", help="Detach on checkout" + ) checkout_if_exists.add_argument( - '-n','--dry-run', - action = 'store_true', - help = 'Dry-run only (lists repos where branch exists)') + "-n", + "--dry-run", + action="store_true", + help="Dry-run only (lists repos where branch exists)", + ) def __changed_files(self): changed_files = self.subparsers.add_parser( - 'changed-files', - description = 'List files that have changes versus the state. By default runs against all components.', - aliases=mepoconfig.get_command_alias('changed-files')) + "changed-files", + description="List files that have changes versus the state. By default runs against all components.", + aliases=mepoconfig.get_command_alias("changed-files"), + ) changed_files.add_argument( - '--full-path', - action = 'store_true', - help = 'Print with full path') + "--full-path", action="store_true", help="Print with full path" + ) changed_files.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to list branches in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to list branches in", + ) def __fetch(self): fetch = self.subparsers.add_parser( - 'fetch', - description = 'Download objects and refs from in component `comp-name`. ' - 'If no components listed, fetches from all', - aliases=mepoconfig.get_command_alias('fetch')) - fetch.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = "Components to fetch in") + "fetch", + description="Download objects and refs from in component `comp-name`. " + "If no components listed, fetches from all", + aliases=mepoconfig.get_command_alias("fetch"), + ) fetch.add_argument( - '--all', - action = 'store_true', - help = 'Fetch all remotes.') + "comp_name", metavar="comp-name", nargs="*", help="Components to fetch in" + ) + fetch.add_argument("--all", action="store_true", help="Fetch all remotes.") fetch.add_argument( - '-p','--prune', - action = 'store_true', - help = 'Prune remote branches.') - fetch.add_argument( - '-t','--tags', - action = 'store_true', - help = 'Fetch tags.') - fetch.add_argument( - '-f','--force', - action = 'store_true', - help = 'Force action.') + "-p", "--prune", action="store_true", help="Prune remote branches." + ) + fetch.add_argument("-t", "--tags", action="store_true", help="Fetch tags.") + fetch.add_argument("-f", "--force", action="store_true", help="Force action.") def __branch(self): branch = self.subparsers.add_parser( - 'branch', - description = "Runs branch commands.", - aliases=mepoconfig.get_command_alias('branch')) + "branch", + description="Runs branch commands.", + aliases=mepoconfig.get_command_alias("branch"), + ) MepoBranchArgParser(branch) def __stash(self): stash = self.subparsers.add_parser( - 'stash', - description = "Runs stash commands.", - aliases=mepoconfig.get_command_alias('stash')) + "stash", + description="Runs stash commands.", + aliases=mepoconfig.get_command_alias("stash"), + ) MepoStashArgParser(stash) def __tag(self): tag = self.subparsers.add_parser( - 'tag', - description = "Runs tag commands.", - aliases=mepoconfig.get_command_alias('tag')) + "tag", + description="Runs tag commands.", + aliases=mepoconfig.get_command_alias("tag"), + ) MepoTagArgParser(tag) def __develop(self): develop = self.subparsers.add_parser( - 'develop', - description = "Checkout current version of 'develop' branches of specified components", - aliases=mepoconfig.get_command_alias('develop')) + "develop", + description="Checkout current version of 'develop' branches of specified components", + aliases=mepoconfig.get_command_alias("develop"), + ) develop.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - default = None, - help = "Component(s) to checkout development branches") + "comp_name", + metavar="comp-name", + nargs="+", + default=None, + help="Component(s) to checkout development branches", + ) develop.add_argument( - '-q','--quiet', - action = 'store_true', - help = 'Suppress prints') + "-q", "--quiet", action="store_true", help="Suppress prints" + ) def __pull(self): pull = self.subparsers.add_parser( - 'pull', - description = "Pull branches of specified components", - aliases=mepoconfig.get_command_alias('pull')) - pull.add_argument('comp_name', - metavar = 'comp-name', - nargs = '+', - default = None, - help = "Components to pull in") + "pull", + description="Pull branches of specified components", + aliases=mepoconfig.get_command_alias("pull"), + ) pull.add_argument( - '-q','--quiet', - action = 'store_true', - help = 'Suppress prints') + "comp_name", + metavar="comp-name", + nargs="+", + default=None, + help="Components to pull in", + ) + pull.add_argument("-q", "--quiet", action="store_true", help="Suppress prints") def __pull_all(self): pull_all = self.subparsers.add_parser( - 'pull-all', - description = "Pull branches of all components (only those in non-detached HEAD state)", - aliases=mepoconfig.get_command_alias('pull-all')) + "pull-all", + description="Pull branches of all components (only those in non-detached HEAD state)", + aliases=mepoconfig.get_command_alias("pull-all"), + ) pull_all.add_argument( - '-q','--quiet', - action = 'store_true', - help = 'Suppress prints') + "-q", "--quiet", action="store_true", help="Suppress prints" + ) def __compare(self): compare = self.subparsers.add_parser( - 'compare', - description = 'Compare current and original states of all components. ' - 'Will only show differing repos unless --all is passed in', - aliases=mepoconfig.get_command_alias('compare')) + "compare", + description="Compare current and original states of all components. " + "Will only show differing repos unless --all is passed in", + aliases=mepoconfig.get_command_alias("compare"), + ) compare.add_argument( - '--all', - action = 'store_true', - help = 'Show all repos, not only differing repos') + "--all", + action="store_true", + help="Show all repos, not only differing repos", + ) compare.add_argument( - '--nocolor', - action = 'store_true', - help = 'Tells command to not display colors.') + "--nocolor", + action="store_true", + help="Tells command to not display colors.", + ) compare.add_argument( - '--wrap', - action = 'store_true', - help = 'Tells command to ignore terminal size and wrap') + "--wrap", + action="store_true", + help="Tells command to ignore terminal size and wrap", + ) def __reset(self): reset = self.subparsers.add_parser( - 'reset', - description = 'Reset the current mepo clone to the original state. ' - 'This will delete all subrepos and does not check for uncommitted changes! ' - 'Must be run in the root of the mepo clone.', - aliases=mepoconfig.get_command_alias('reset')) - reset.add_argument( - '-f','--force', - action = 'store_true', - help = 'Force action.') + "reset", + description="Reset the current mepo clone to the original state. " + "This will delete all subrepos and does not check for uncommitted changes! " + "Must be run in the root of the mepo clone.", + aliases=mepoconfig.get_command_alias("reset"), + ) + reset.add_argument("-f", "--force", action="store_true", help="Force action.") reset.add_argument( - '--reclone', - action = 'store_true', - help = 'Reclone repos after reset.') - reset.add_argument( - '-n','--dry-run', - action = 'store_true', - help = 'Dry-run only') + "--reclone", action="store_true", help="Reclone repos after reset." + ) + reset.add_argument("-n", "--dry-run", action="store_true", help="Dry-run only") def __whereis(self): whereis = self.subparsers.add_parser( - 'whereis', - description = 'Get the location of component `comp-name` ' - 'relative to my current location. If `comp-name` is not present, ' - 'get the relative locations of ALL components.', - aliases=mepoconfig.get_command_alias('whereis')) + "whereis", + description="Get the location of component `comp-name` " + "relative to my current location. If `comp-name` is not present, " + "get the relative locations of ALL components.", + aliases=mepoconfig.get_command_alias("whereis"), + ) whereis.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '?', - default = None, - help = "Component to get location of") + "comp_name", + metavar="comp-name", + nargs="?", + default=None, + help="Component to get location of", + ) whereis.add_argument( - '-i','--ignore-case', - action = 'store_true', - help = 'Ignore case for whereis') + "-i", "--ignore-case", action="store_true", help="Ignore case for whereis" + ) def __stage(self): stage = self.subparsers.add_parser( - 'stage', - description = 'Stage modified & untracked files in the specified component(s)', - aliases=mepoconfig.get_command_alias('stage')) + "stage", + description="Stage modified & untracked files in the specified component(s)", + aliases=mepoconfig.get_command_alias("stage"), + ) stage.add_argument( - '--untracked', - action = 'store_true', - help = 'Stage untracked files as well') + "--untracked", action="store_true", help="Stage untracked files as well" + ) stage.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to stage file in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to stage file in", + ) def __unstage(self): unstage = self.subparsers.add_parser( - 'unstage', - description = 'Un-stage staged files. ' - 'If a component is specified, files are un-staged only for that component.', - aliases=mepoconfig.get_command_alias('unstage')) + "unstage", + description="Un-stage staged files. " + "If a component is specified, files are un-staged only for that component.", + aliases=mepoconfig.get_command_alias("unstage"), + ) unstage.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to unstage in', - default = None) + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to unstage in", + default=None, + ) def __commit(self): commit = self.subparsers.add_parser( - 'commit', - description = 'Commit staged files in the specified components', - aliases=mepoconfig.get_command_alias('commit')) + "commit", + description="Commit staged files in the specified components", + aliases=mepoconfig.get_command_alias("commit"), + ) commit.add_argument( - '-a', '--all', - action = 'store_true', - help = 'Stage all tracked files and then commit') + "-a", + "--all", + action="store_true", + help="Stage all tracked files and then commit", + ) commit.add_argument( - '-m', '--message', + "-m", + "--message", type=str, - metavar = 'message', + metavar="message", default=None, - help = "Message to commit with") + help="Message to commit with", + ) commit.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to commit file in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to commit file in", + ) def __push(self): push = self.subparsers.add_parser( - 'push', - description = 'Push local commits to remote for specified component. ' - 'Use mepo tag push to push tags', - aliases=mepoconfig.get_command_alias('push')) + "push", + description="Push local commits to remote for specified component. " + "Use mepo tag push to push tags", + aliases=mepoconfig.get_command_alias("push"), + ) push.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to push to remote') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to push to remote", + ) def __save(self): save = self.subparsers.add_parser( - 'save', - description = 'Save current state in a yaml config file', - aliases=mepoconfig.get_command_alias('save')) + "save", + description="Save current state in a yaml registry", + aliases=mepoconfig.get_command_alias("save"), + ) save.add_argument( - 'config_file', - metavar = 'config-file', - nargs = '?', - default = 'components-new.yaml', - help = 'default: %(default)s') + "registry", + metavar="registry", + nargs="?", + default="components-new.yaml", + help="default: %(default)s", + ) def __config(self): config = self.subparsers.add_parser( - 'config', - description = "Runs config commands.", - aliases=mepoconfig.get_command_alias('config')) + "config", + description="Runs config commands.", + aliases=mepoconfig.get_command_alias("config"), + ) MepoConfigArgParser(config) + + def __update_state(self): + update_state = self.subparsers.add_parser( + "update-state", + description="Permanently update mepo1 state to current", + aliases=mepoconfig.get_command_alias("update-state"), + ) diff --git a/src/mepo/cmdline/stash_parser.py b/src/mepo/cmdline/stash_parser.py index 0d34920..af0bb4a 100644 --- a/src/mepo/cmdline/stash_parser.py +++ b/src/mepo/cmdline/stash_parser.py @@ -1,69 +1,75 @@ import argparse + class MepoStashArgParser(object): def __init__(self, stash): self.stash = stash.add_subparsers() - self.stash.title = 'mepo stash sub-commands' - self.stash.dest = 'mepo_stash_cmd' + self.stash.title = "mepo stash sub-commands" + self.stash.dest = "mepo_stash_cmd" self.stash.required = True self.__push() self.__list() self.__pop() self.__apply() self.__show() - + def __push(self): stpush = self.stash.add_parser( - 'push', - description = 'Push (create) stash in component ') + "push", description="Push (create) stash in component " + ) stpush.add_argument( - '-m', '--message', - type=str, - metavar = 'message', + "-m", + "--message", + type=str, + metavar="message", default=None, - help = 'Message for the stash') + help="Message for the stash", + ) stpush.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to push stash in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to push stash in", + ) def __show(self): stshow = self.stash.add_parser( - 'show', - description = 'show stash in component ') + "show", description="show stash in component " + ) stshow.add_argument( - '-p', '--patch', - action = 'store_true', - help = 'Message for the stash') + "-p", "--patch", action="store_true", help="Message for the stash" + ) stshow.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to show stash in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to show stash in", + ) def __list(self): stlist = self.stash.add_parser( - 'list', - description = 'List local stashes of all components') + "list", description="List local stashes of all components" + ) def __pop(self): stpop = self.stash.add_parser( - 'pop', - description = 'Pop stash in component ') + "pop", description="Pop stash in component " + ) stpop.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to pop stash in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to pop stash in", + ) def __apply(self): stapply = self.stash.add_parser( - 'apply', - description = 'apply stash in component ') + "apply", description="apply stash in component " + ) stapply.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '+', - help = 'Component to apply stash in') + "comp_name", + metavar="comp-name", + nargs="+", + help="Component to apply stash in", + ) diff --git a/src/mepo/cmdline/tag_parser.py b/src/mepo/cmdline/tag_parser.py index 0be42e7..dfacfc0 100644 --- a/src/mepo/cmdline/tag_parser.py +++ b/src/mepo/cmdline/tag_parser.py @@ -1,11 +1,12 @@ import argparse + class MepoTagArgParser(object): def __init__(self, tag): self.tag = tag.add_subparsers() - self.tag.title = 'mepo tag sub-commands' - self.tag.dest = 'mepo_tag_cmd' + self.tag.title = "mepo tag sub-commands" + self.tag.dest = "mepo_tag_cmd" self.tag.required = True self.__list() self.__create() @@ -14,71 +15,68 @@ def __init__(self, tag): def __list(self): tglist = self.tag.add_parser( - 'list', - description = 'List tags. If no component is specified, runs over all components') + "list", + description="List tags. If no component is specified, runs over all components", + ) tglist.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to list tags in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to list tags in", + ) def __create(self): create = self.tag.add_parser( - 'create', - description = 'Create tag in component . If no component is specified, runs over all components') - create.add_argument( - 'tag_name', - metavar = 'tag-name', - help = "Name of tag") + "create", + description="Create tag in component . If no component is specified, runs over all components", + ) + create.add_argument("tag_name", metavar="tag-name", help="Name of tag") create.add_argument( - '-a', '--annotate', - action = 'store_true', - help = "Make an annotated tag") + "-a", "--annotate", action="store_true", help="Make an annotated tag" + ) create.add_argument( - '-m', '--message', + "-m", + "--message", type=str, - metavar = 'message', - default = None, - help = "Message for the tag" - ) + metavar="message", + default=None, + help="Message for the tag", + ) create.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to create tags in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to create tags in", + ) def __delete(self): delete = self.tag.add_parser( - 'delete', - description = 'Delete tag in component . If no component is specified, runs over all components') + "delete", + description="Delete tag in component . If no component is specified, runs over all components", + ) + delete.add_argument("tag_name", metavar="tag-name", help="Name of tag") delete.add_argument( - 'tag_name', - metavar = 'tag-name', - help = "Name of tag") - delete.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to delete tags in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to delete tags in", + ) def __push(self): push = self.tag.add_parser( - 'push', - description = 'Push tag in component . If no component is specified, runs over all components') - push.add_argument( - 'tag_name', - metavar = 'tag-name', - help = "Name of tag") + "push", + description="Push tag in component . If no component is specified, runs over all components", + ) + push.add_argument("tag_name", metavar="tag-name", help="Name of tag") push.add_argument( - '-f', '--force', - action = 'store_true', - help = "Force push (be careful!)") + "-f", "--force", action="store_true", help="Force push (be careful!)" + ) push.add_argument( - '-d', '--delete', - action = 'store_true', - help = "Delete (be careful!)") + "-d", "--delete", action="store_true", help="Delete (be careful!)" + ) push.add_argument( - 'comp_name', - metavar = 'comp-name', - nargs = '*', - help = 'Component to push tags in') + "comp_name", + metavar="comp-name", + nargs="*", + help="Component to push tags in", + ) diff --git a/src/mepo/command/__init__.py b/src/mepo/command/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mepo/command/branch.py b/src/mepo/command/branch.py new file mode 100644 index 0000000..b419358 --- /dev/null +++ b/src/mepo/command/branch.py @@ -0,0 +1,12 @@ +from .branch_list import run as branch_list_run +from .branch_create import run as branch_create_run +from .branch_delete import run as branch_delete_run + + +def run(args): + d = { + "list": branch_list_run, + "create": branch_create_run, + "delete": branch_delete_run, + } + d[args.mepo_branch_cmd](args) diff --git a/src/mepo/command/branch/branch.py b/src/mepo/command/branch/branch.py deleted file mode 100644 index 961465c..0000000 --- a/src/mepo/command/branch/branch.py +++ /dev/null @@ -1,15 +0,0 @@ -import subprocess as sp - -from mepo.state.state import MepoState - -from mepo.command.branch.list import list -from mepo.command.branch.create import create -from mepo.command.branch.delete import delete - -def run(args): - d = { - 'list': list, - 'create': create, - 'delete': delete, - } - d[args.mepo_branch_cmd].run(args) diff --git a/src/mepo/command/branch/create/create.py b/src/mepo/command/branch_create.py similarity index 62% rename from src/mepo/command/branch/create/create.py rename to src/mepo/command/branch_create.py index ba17415..d22ec96 100644 --- a/src/mepo/command/branch/create/create.py +++ b/src/mepo/command/branch_create.py @@ -1,6 +1,7 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() @@ -9,4 +10,4 @@ def run(args): for comp in comps2crtbr: git = GitRepository(comp.remote, comp.local) git.create_branch(args.branch_name) - print('+ {}: {}'.format(comp.name, args.branch_name)) + print(f"+ {comp.name}: {args.branch_name}") diff --git a/src/mepo/command/branch/delete/delete.py b/src/mepo/command/branch_delete.py similarity index 63% rename from src/mepo/command/branch/delete/delete.py rename to src/mepo/command/branch_delete.py index dc60bc9..5c6f718 100644 --- a/src/mepo/command/branch/delete/delete.py +++ b/src/mepo/command/branch_delete.py @@ -1,6 +1,7 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() @@ -9,4 +10,4 @@ def run(args): for comp in comps2delbr: git = GitRepository(comp.remote, comp.local) git.delete_branch(args.branch_name, args.force) - print('- {}: {}'.format(comp.name, args.branch_name)) + print("- {}: {}".format(comp.name, args.branch_name)) diff --git a/src/mepo/command/tag/list/list.py b/src/mepo/command/branch_list.py similarity index 68% rename from src/mepo/command/tag/list/list.py rename to src/mepo/command/branch_list.py index a7c2a98..62972e1 100644 --- a/src/mepo/command/tag/list/list.py +++ b/src/mepo/command/branch_list.py @@ -1,18 +1,20 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() comps2list = _get_comps_to_list(args.comp_name, allcomps) max_namelen = len(max([x.name for x in comps2list], key=len)) - FMT = '{:<%s.%ss} | {: columns): - print(FMT1.format(name, orig + ' ...')) - print(FMT2.format(name_blank, '...', curr)) + print(FMT1.format(name, orig + " ...")) + print(FMT2.format(name_blank, "...", curr)) else: print(FMT0.format(name, orig, curr)) - diff --git a/src/mepo/command/config.py b/src/mepo/command/config.py new file mode 100644 index 0000000..0b0c7d0 --- /dev/null +++ b/src/mepo/command/config.py @@ -0,0 +1,14 @@ +from .config_get import run as config_get_run +from .config_set import run as config_set_run +from .config_delete import run as config_delete_run +from .config_print import run as config_print_run + + +def run(args): + d = { + "get": config_get_run, + "set": config_set_run, + "delete": config_delete_run, + "print": config_print_run, + } + d[args.mepo_config_cmd](args) diff --git a/src/mepo/command/config/config.py b/src/mepo/command/config/config.py deleted file mode 100644 index 211b7da..0000000 --- a/src/mepo/command/config/config.py +++ /dev/null @@ -1,17 +0,0 @@ -import subprocess as sp - -from mepo.state.state import MepoState - -from mepo.command.config.get import get -from mepo.command.config.set import set -from mepo.command.config.delete import delete -from mepo.command.config.print import print - -def run(args): - d = { - 'get': get, - 'set': set, - 'delete': delete, - 'print': print - } - d[args.mepo_config_cmd].run(args) diff --git a/src/mepo/command/config/get/get.py b/src/mepo/command/config/get/get.py deleted file mode 100644 index e88d145..0000000 --- a/src/mepo/command/config/get/get.py +++ /dev/null @@ -1,13 +0,0 @@ -from mepo.utilities import mepoconfig - -def run(args): - section, option = mepoconfig.split_entry(args.entry) - if not mepoconfig.has_section(section): - raise Exception(f'Section [{section}] does not exist in .mepoconfig') - if not mepoconfig.has_option(section, option): - raise Exception(f'Option [{option}] does not exist in section [{section}] in .mepoconfig') - value = mepoconfig.get(section, option) - print(f''' - [{section}] - {option} = {value} - ''') diff --git a/src/mepo/command/config/delete/delete.py b/src/mepo/command/config_delete.py similarity index 78% rename from src/mepo/command/config/delete/delete.py rename to src/mepo/command/config_delete.py index b7b85b2..11734c7 100644 --- a/src/mepo/command/config/delete/delete.py +++ b/src/mepo/command/config_delete.py @@ -1,4 +1,5 @@ -from mepo.utilities import mepoconfig +from ..utilities import mepoconfig + def run(args): section, option = mepoconfig.split_entry(args.entry) diff --git a/src/mepo/command/config_get.py b/src/mepo/command/config_get.py new file mode 100644 index 0000000..c278089 --- /dev/null +++ b/src/mepo/command/config_get.py @@ -0,0 +1,18 @@ +from ..utilities import mepoconfig + + +def run(args): + section, option = mepoconfig.split_entry(args.entry) + if not mepoconfig.has_section(section): + raise Exception(f"Section [{section}] does not exist in .mepoconfig") + if not mepoconfig.has_option(section, option): + raise Exception( + f"Option [{option}] does not exist in section [{section}] in .mepoconfig" + ) + value = mepoconfig.get(section, option) + print( + f""" + [{section}] + {option} = {value} + """ + ) diff --git a/src/mepo/command/config/print/print.py b/src/mepo/command/config_print.py similarity index 50% rename from src/mepo/command/config/print/print.py rename to src/mepo/command/config_print.py index 1a612c3..6c78542 100644 --- a/src/mepo/command/config/print/print.py +++ b/src/mepo/command/config_print.py @@ -1,4 +1,5 @@ -from mepo.utilities import mepoconfig +from ..utilities import mepoconfig + def run(args): mepoconfig.print() diff --git a/src/mepo/command/config/set/set.py b/src/mepo/command/config_set.py similarity index 81% rename from src/mepo/command/config/set/set.py rename to src/mepo/command/config_set.py index 80d9079..94e4bf3 100644 --- a/src/mepo/command/config/set/set.py +++ b/src/mepo/command/config_set.py @@ -1,4 +1,5 @@ -from mepo.utilities import mepoconfig +from ..utilities import mepoconfig + def run(args): section, option = mepoconfig.split_entry(args.entry) diff --git a/src/mepo/command/develop/develop.py b/src/mepo/command/develop.py similarity index 54% rename from src/mepo/command/develop/develop.py rename to src/mepo/command/develop.py index aec6f28..eb61127 100644 --- a/src/mepo/command/develop/develop.py +++ b/src/mepo/command/develop.py @@ -1,7 +1,8 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository -from mepo.utilities import colors +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository +from ..utilities import colors + def run(args): allcomps = MepoState.read_state() @@ -12,8 +13,12 @@ def run(args): if comp.develop is None: raise Exception("'develop' branch not specified for {}".format(comp.name)) if not args.quiet: - print("Checking out development branch %s in %s" % - (colors.YELLOW + comp.develop + colors.RESET, - colors.RESET + comp.name + colors.RESET)) + print( + "Checking out development branch %s in %s" + % ( + colors.YELLOW + comp.develop + colors.RESET, + colors.RESET + comp.name + colors.RESET, + ) + ) git.checkout(comp.develop) output = git.pull() diff --git a/src/mepo/command/diff/diff.py b/src/mepo/command/diff.py similarity index 75% rename from src/mepo/command/diff/diff.py rename to src/mepo/command/diff.py index 6afcd40..118d98f 100644 --- a/src/mepo/command/diff/diff.py +++ b/src/mepo/command/diff.py @@ -5,9 +5,10 @@ from shutil import get_terminal_size -from mepo.state.state import MepoState -from mepo.repository.git import GitRepository -from mepo.utilities import verify +from ..state import MepoState +from ..git import GitRepository +from ..utilities import verify + def run(args): foundDiff = False @@ -19,12 +20,13 @@ def run(args): result = check_component_diff(comp, args) if result: if not foundDiff: - print('Diffing...'); sys.stdout.flush() + print("Diffing...", flush=True) foundDiff = True print_diff(comp, args, result) if not foundDiff: - print('No diffs found') + print("No diffs found") + def _get_comps_to_diff(specified_comps, allcomps): comps_to_diff = allcomps @@ -33,6 +35,7 @@ def _get_comps_to_diff(specified_comps, allcomps): comps_to_diff = [x for x in allcomps if x.name in specified_comps] return comps_to_diff + def check_component_diff(comp, args): git = GitRepository(comp.remote, comp.local) @@ -44,17 +47,19 @@ def check_component_diff(comp, args): _ignore_submodules = None return git.run_diff(args, _ignore_submodules) + def print_diff(comp, args, output): - columns, lines = get_terminal_size(fallback=(80,20)) - horiz_line = u'\u2500'*columns + columns, lines = get_terminal_size(fallback=(80, 20)) + horiz_line = "\u2500" * columns - print("{} (location: {}):".format(comp.name,_get_relative_path(comp.local))) + print("{} (location: {}):".format(comp.name, _get_relative_path(comp.local))) print() - for line in output.split('\n'): - #print(' |', line.rstrip()) + for line in output.split("\n"): + # print(' |', line.rstrip()) print(line.rstrip()) print(horiz_line) + def _get_relative_path(local_path): """ Get the relative path when given a local path. @@ -63,7 +68,7 @@ def _get_relative_path(local_path): """ # This creates a full path on the disk from the root of mepo and the local_path - full_local_path=os.path.join(MepoState.get_root_dir(),local_path) + full_local_path = os.path.join(MepoState.get_root_dir(), local_path) # We return the path relative to where we currently are return os.path.relpath(full_local_path, os.getcwd()) diff --git a/src/mepo/command/fetch/fetch.py b/src/mepo/command/fetch.py similarity index 66% rename from src/mepo/command/fetch/fetch.py rename to src/mepo/command/fetch.py index 299db8c..02c0e3b 100644 --- a/src/mepo/command/fetch/fetch.py +++ b/src/mepo/command/fetch.py @@ -1,17 +1,18 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository -from mepo.utilities import colors +from ..state import MepoState +from ..utilities import colors +from ..utilities import verify +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() comps2fetch = _get_comps_to_list(args.comp_name, allcomps) for comp in comps2fetch: git = GitRepository(comp.remote, comp.local) - print("Fetching %s" % - colors.YELLOW + comp.name + colors.RESET) + print("Fetching %s" % colors.YELLOW + comp.name + colors.RESET) git.fetch(args) + def _get_comps_to_list(specified_comps, allcomps): comps_to_list = allcomps if specified_comps: diff --git a/src/mepo/command/init.py b/src/mepo/command/init.py new file mode 100644 index 0000000..b28eb7f --- /dev/null +++ b/src/mepo/command/init.py @@ -0,0 +1,25 @@ +from ..state import MepoState +from ..utilities import mepoconfig + + +def run(args): + if args.style: + style = args.style + elif mepoconfig.has_option("init", "style"): + allowed_styles = ["naked", "prefix", "postfix"] + style = mepoconfig.get("init", "style") + if style not in allowed_styles: + raise Exception( + f"Detected style [{style}] from .mepoconfig is not an allowed style: {allowed_styles}" + ) + else: + print(f"Found style [{style}] in .mepoconfig") + else: + style = None + + allcomps = MepoState.initialize(args.registry, style) + + if not style: + print(f"Initializing mepo using {args.registry}") + else: + print(f"Initializing mepo using {args.registry} with {style} style") diff --git a/src/mepo/command/init/init.py b/src/mepo/command/init/init.py deleted file mode 100644 index b4ef212..0000000 --- a/src/mepo/command/init/init.py +++ /dev/null @@ -1,22 +0,0 @@ -from mepo.state.state import MepoState -from mepo.utilities import mepoconfig - -def run(args): - if args.style: - style = args.style - elif mepoconfig.has_option('init','style'): - allowed_styles = ['naked','prefix','postfix'] - style = mepoconfig.get('init','style') - if style not in allowed_styles: - raise Exception(f'Detected style [{style}] from .mepoconfig is not an allowed style: {allowed_styles}') - else: - print(f'Found style [{style}] in .mepoconfig') - else: - style = None - - allcomps = MepoState.initialize(args.config,style) - - if not style: - print(f'Initializing mepo using {args.config}') - else: - print(f'Initializing mepo using {args.config} with {style} style') diff --git a/src/mepo/command/list.py b/src/mepo/command/list.py new file mode 100644 index 0000000..bf6513b --- /dev/null +++ b/src/mepo/command/list.py @@ -0,0 +1,9 @@ +from ..state import MepoState + + +def run(args): + _end = "\n" if args.one_per_line else " " + allcomps = MepoState.read_state() + for comp in allcomps[:-1]: + print(comp.name, end=_end) + print(allcomps[-1].name) diff --git a/src/mepo/command/list/list.py b/src/mepo/command/list/list.py deleted file mode 100644 index 25a8e4d..0000000 --- a/src/mepo/command/list/list.py +++ /dev/null @@ -1,7 +0,0 @@ -from mepo.state.state import MepoState - -def run(args): - allcomps = MepoState.read_state() - for comp in allcomps: - print(comp.name, end=' ') - print() diff --git a/src/mepo/command/pull-all.py b/src/mepo/command/pull-all.py new file mode 100644 index 0000000..c54773d --- /dev/null +++ b/src/mepo/command/pull-all.py @@ -0,0 +1,30 @@ +from ..state import MepoState +from ..component import MepoVersion +from ..git import GitRepository +from ..utilities import colors + + +def run(args): + allcomps = MepoState.read_state() + detached_comps = [] + for comp in allcomps: + git = GitRepository(comp.remote, comp.local) + name, tYpe, detached = MepoVersion(*git.get_version()) + if detached: + detached_comps.append(comp.name) + else: + print( + "Pulling branch %s in %s " + % ( + colors.YELLOW + name + colors.RESET, + colors.RESET + comp.name + colors.RESET, + ) + ) + output = git.pull() + if not args.quiet: + print(output) + if len(detached_comps) > 0: + print( + "The following repos were not pulled (detached HEAD): %s" + % (", ".join(map(str, detached_comps))) + ) diff --git a/src/mepo/command/pull-all/pull-all.py b/src/mepo/command/pull-all/pull-all.py deleted file mode 100644 index a47bf69..0000000 --- a/src/mepo/command/pull-all/pull-all.py +++ /dev/null @@ -1,22 +0,0 @@ -from mepo.state.state import MepoState -from mepo.repository.git import GitRepository -from mepo.state.component import MepoVersion -from mepo.utilities import colors - -def run(args): - allcomps = MepoState.read_state() - detached_comps=[] - for comp in allcomps: - git = GitRepository(comp.remote, comp.local) - name, tYpe, detached = MepoVersion(*git.get_version()) - if detached: - detached_comps.append(comp.name) - else: - print("Pulling branch %s in %s " % - (colors.YELLOW + name + colors.RESET, - colors.RESET + comp.name + colors.RESET)) - output = git.pull() - if not args.quiet: print(output) - if len(detached_comps) > 0: - print("The following repos were not pulled (detached HEAD): %s" % (', '.join(map(str, detached_comps)))) - diff --git a/src/mepo/command/pull.py b/src/mepo/command/pull.py new file mode 100644 index 0000000..f3bb97c --- /dev/null +++ b/src/mepo/command/pull.py @@ -0,0 +1,27 @@ +from ..state import MepoState +from ..component import MepoVersion +from ..utilities import verify +from ..utilities import colors +from ..git import GitRepository + + +def run(args): + allcomps = MepoState.read_state() + verify.valid_components(args.comp_name, allcomps) + comps2pull = [x for x in allcomps if x.name in args.comp_name] + for comp in comps2pull: + git = GitRepository(comp.remote, comp.local) + name, tYpe, is_detached = MepoVersion(*git.get_version()) + if is_detached: + raise Exception("{} has detached head! Cannot pull.".format(comp.name)) + else: + print( + "Pulling branch %s in %s " + % ( + colors.YELLOW + name + colors.RESET, + colors.RESET + comp.name + colors.RESET, + ) + ) + output = git.pull() + if not args.quiet: + print(output) diff --git a/src/mepo/command/pull/pull.py b/src/mepo/command/pull/pull.py deleted file mode 100644 index f934e80..0000000 --- a/src/mepo/command/pull/pull.py +++ /dev/null @@ -1,21 +0,0 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository -from mepo.state.component import MepoVersion -from mepo.utilities import colors - -def run(args): - allcomps = MepoState.read_state() - verify.valid_components(args.comp_name, allcomps) - comps2pull = [x for x in allcomps if x.name in args.comp_name] - for comp in comps2pull: - git = GitRepository(comp.remote, comp.local) - name, tYpe, is_detached = MepoVersion(*git.get_version()) - if is_detached: - raise Exception('{} has detached head! Cannot pull.'.format(comp.name)) - else: - print("Pulling branch %s in %s " % - (colors.YELLOW + name + colors.RESET, - colors.RESET + comp.name + colors.RESET)) - output = git.pull() - if not args.quiet: print(output) diff --git a/src/mepo/command/push/push.py b/src/mepo/command/push.py similarity index 63% rename from src/mepo/command/push/push.py rename to src/mepo/command/push.py index 3b7d27a..78869d6 100644 --- a/src/mepo/command/push/push.py +++ b/src/mepo/command/push.py @@ -1,6 +1,7 @@ -from mepo.utilities import verify -from mepo.state.state import MepoState -from mepo.repository.git import GitRepository +from ..utilities import verify +from ..state import MepoState +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() @@ -9,5 +10,5 @@ def run(args): for comp in comps2push: git = GitRepository(comp.remote, comp.local) output = git.push() - print('----------\nPushed: {}\n----------'.format(comp.name)) + print("----------\nPushed: {}\n----------".format(comp.name)) print(output) diff --git a/src/mepo/command/reset/reset.py b/src/mepo/command/reset.py similarity index 61% rename from src/mepo/command/reset/reset.py rename to src/mepo/command/reset.py index 7ea0104..1f36025 100644 --- a/src/mepo/command/reset/reset.py +++ b/src/mepo/command/reset.py @@ -1,16 +1,17 @@ import os import shutil -from mepo.state.state import MepoState -from mepo.state.exceptions import NotInRootDirError +from ..state import MepoState +from ..utilities.exceptions import NotInRootDirError -from mepo.command.clone import clone as mepo_clone +from .clone import run as mepo_clone_run # This command will "reset" the mepo clone. This will delete all -# the subrepos, remove the .mepo directory, and then re-clone all the +# the subrepos, remove the mepo state directory, and then re-clone all the # subrepos. This is useful if you want to start over with a fresh clone # of the project. + def run(args): allcomps = MepoState.read_state() @@ -21,14 +22,19 @@ def run(args): curdir = os.getcwd() ## Then check that they are the same, if they are not, then throw a NotInRootDirError if rootdir != curdir: - raise NotInRootDirError('Error! As a safety precaution, you must be in the root directory of the project to reset') + raise NotInRootDirError( + "Error! As a safety precaution, you must be in the root directory of the project to reset" + ) # If we get this far, then we are in the root directory of the project # If a user has called this command without the force flag, we # will ask them to confirm that they want to reset the project if not args.force and not args.dry_run: - print(f"Are you sure you want to reset the project? If so, type 'yes' and press enter.", end=' ') + print( + f"Are you sure you want to reset the project? If so, type 'yes' and press enter.", + end=" ", + ) answer = input() if answer != "yes": print("Reset cancelled.") @@ -43,21 +49,21 @@ def run(args): else: # Get the relative path to the component relpath = _get_relative_path(comp.local) - print(f'Removing {relpath}', end='...') + print(f"Removing {relpath}", end="...") # Remove the component if not dry run if not args.dry_run: shutil.rmtree(relpath) - print('done.') + print("done.") else: - print(f'dry-run only. Not removing {relpath}') + print(f"dry-run only. Not removing {relpath}") # Next, we need to remove the .mepo directory - print(f'Removing .mepo', end='...') + print(f"Removing mepo state", end="...") if not args.dry_run: - shutil.rmtree('.mepo') - print('done.') + shutil.rmtree(MepoState.get_dir()) + print("done.") else: - print(f'dry-run only. Not removing .mepo') + print(f"dry-run only. Not removing mepo state") # If they pass in the --reclone flag, then we will re-clone all the subrepos if args.reclone: @@ -65,23 +71,35 @@ def run(args): # mepo_clone requires args which is an Argparse Namespace object # We will create a new Namespace object with the correct arguments # for mepo_clone - clone_args = type('Namespace', (object,), {'repo_url': None, 'directory': None, 'branch': None, 'config': None, 'allrepos': False, 'style': None}) + clone_args = type( + "Namespace", + (object,), + { + "repo_url": None, + "directory": None, + "branch": None, + "registry": None, + "allrepos": False, + "style": None, + }, + ) if not args.dry_run: - print('Re-cloning all subrepos') - mepo_clone.run(clone_args) - print('Recloning done.') + print("Re-cloning all subrepos") + mepo_clone_run(clone_args) + print("Recloning done.") else: - print(f'Dry-run only. Not re-cloning all subrepos') + print(f"Dry-run only. Not re-cloning all subrepos") + def _get_relative_path(local_path): """ Get the relative path when given a local path. - local_path: The path to a subrepo as known by mepo (relative to the .mepo directory) + local_path: The path to a subrepo as known by mepo (relative to the mepo state directory) """ # This creates a full path on the disk from the root of mepo and the local_path - full_local_path=os.path.join(MepoState.get_root_dir(),local_path) + full_local_path = os.path.join(MepoState.get_root_dir(), local_path) # We return the path relative to where we currently are return os.path.relpath(full_local_path, os.getcwd()) diff --git a/src/mepo/command/restore-state.py b/src/mepo/command/restore-state.py new file mode 100644 index 0000000..e4bd34e --- /dev/null +++ b/src/mepo/command/restore-state.py @@ -0,0 +1,41 @@ +import sys +import time +import multiprocessing as mp + +from ..state import MepoState +from ..git import GitRepository +from ..utilities.version import version_to_string +from ..utilities import colors + + +def run(args): + print("Checking status...", flush=True) + allcomps = MepoState.read_state() + with mp.Pool() as pool: + result = pool.map(check_component_status, allcomps) + restore_state(allcomps, result) + + +def check_component_status(comp): + git = GitRepository(comp.remote, comp.local) + curr_ver = version_to_string(git.get_version(), git) + return (curr_ver, git.check_status()) + + +def restore_state(allcomps, result): + for index, comp in enumerate(allcomps): + git = GitRepository(comp.remote, comp.local) + current_version = result[index][0].split(" ")[1] + orig_version = comp.version.name + if current_version != orig_version: + print( + colors.YELLOW + + "Restoring " + + colors.RESET + + "{} to {} from {}.".format( + comp.name, + colors.GREEN + orig_version + colors.RESET, + colors.RED + current_version + colors.RESET, + ) + ) + git.checkout(comp.version.name) diff --git a/src/mepo/command/restore-state/restore-state.py b/src/mepo/command/restore-state/restore-state.py deleted file mode 100644 index b713bf3..0000000 --- a/src/mepo/command/restore-state/restore-state.py +++ /dev/null @@ -1,31 +0,0 @@ -import sys -import time -import multiprocessing as mp -import atexit - -from mepo.state.state import MepoState -from mepo.repository.git import GitRepository -from mepo.utilities.version import version_to_string -from mepo.utilities import colors - -def run(args): - print('Checking status...'); sys.stdout.flush() - allcomps = MepoState.read_state() - pool = mp.Pool() - atexit.register(pool.close) - result = pool.map(check_component_status, allcomps) - restore_state(allcomps, result) - -def check_component_status(comp): - git = GitRepository(comp.remote, comp.local) - curr_ver = version_to_string(git.get_version(),git) - return (curr_ver, git.check_status()) - -def restore_state(allcomps, result): - for index, comp in enumerate(allcomps): - git = GitRepository(comp.remote, comp.local) - current_version = result[index][0].split(' ')[1] - orig_version = comp.version.name - if current_version != orig_version: - print(colors.YELLOW + "Restoring " + colors.RESET + "{} to {} from {}.".format(comp.name, colors.GREEN + orig_version + colors.RESET, colors.RED + current_version + colors.RESET)) - git.checkout(comp.version.name) diff --git a/src/mepo/command/save/save.py b/src/mepo/command/save.py similarity index 50% rename from src/mepo/command/save/save.py rename to src/mepo/command/save.py index 44f3edf..0e678b5 100644 --- a/src/mepo/command/save/save.py +++ b/src/mepo/command/save.py @@ -1,11 +1,12 @@ -from mepo.state.state import MepoState -from mepo.state.component import MepoVersion -from mepo.repository.git import GitRepository -from mepo.config.config_file import ConfigFile -from mepo.utilities.version import sanitize_version_string - import os +from ..state import MepoState +from ..component import MepoVersion +from ..git import GitRepository +from ..registry import Registry +from ..utilities.version import sanitize_version_string + + def run(args): allcomps = MepoState.read_state() for comp in allcomps: @@ -16,50 +17,63 @@ def run(args): complist = dict() relpath_start = MepoState.get_root_dir() for comp in allcomps: - complist.update(comp.to_dict(relpath_start)) - config_file_root_dir=os.path.join(relpath_start,args.config_file) - ConfigFile(config_file_root_dir).write_yaml(complist) - print(f"Components written to '{config_file_root_dir}'") + complist.update(comp.to_registry_format()) + registry_root_dir = os.path.join(relpath_start, args.registry) + Registry(registry_root_dir).write_yaml(complist) + print(f"Components written to '{registry_root_dir}'") + def _update_comp(comp): git = GitRepository(comp.remote, comp.local) orig_ver = comp.version curr_ver = MepoVersion(*git.get_version()) - orig_ver_is_tag_or_hash = (orig_ver.type == 't' or orig_ver.type == 'h') - curr_ver_is_tag_or_hash = (curr_ver.type == 't' or curr_ver.type == 'h') + orig_ver_is_tag_or_hash = orig_ver.type == "t" or orig_ver.type == "h" + curr_ver_is_tag_or_hash = curr_ver.type == "t" or curr_ver.type == "h" if orig_ver_is_tag_or_hash and curr_ver_is_tag_or_hash: # This command is to try and work with git tag oddities - curr_ver_to_use = sanitize_version_string(orig_ver.name,curr_ver.name,git) + curr_ver_to_use = sanitize_version_string(orig_ver.name, curr_ver.name, git) if curr_ver_to_use == orig_ver.name: comp.version = orig_ver else: - _verify_local_and_remote_commit_ids_match(git, curr_ver_to_use, comp.name, curr_ver.type) + _verify_local_and_remote_commit_ids_match( + git, curr_ver_to_use, comp.name, curr_ver.type + ) comp.version = curr_ver else: if _version_has_changed(curr_ver, orig_ver, comp.name): - _verify_local_and_remote_commit_ids_match(git, curr_ver.name, comp.name, curr_ver.type) + _verify_local_and_remote_commit_ids_match( + git, curr_ver.name, comp.name, curr_ver.type + ) comp.version = curr_ver + def _version_has_changed(curr_ver, orig_ver, name): result = False if curr_ver != orig_ver: - if curr_ver.type == 'b': - assert curr_ver.detached is False, f'You cannot save a detached branch, have you committed your code in {name}?\n {curr_ver}' + if curr_ver.type == "b": + assert ( + curr_ver.detached is False + ), f"You cannot save a detached branch, have you committed your code in {name}?\n {curr_ver}" result = True - elif curr_ver.type == 't': + elif curr_ver.type == "t": result = True - elif curr_ver.type == 'h': + elif curr_ver.type == "h": result = True else: raise Exception("This should not happen") return result -def _verify_local_and_remote_commit_ids_match(git, curr_ver_name, comp_name, curr_ver_type): + +def _verify_local_and_remote_commit_ids_match( + git, curr_ver_name, comp_name, curr_ver_type +): remote_id = git.get_remote_latest_commit_id(curr_ver_name, curr_ver_type) local_id = git.get_local_latest_commit_id() - failmsg = "{} (remote commit) != {} (local commit) for {}:{}. Did you try 'mepo push'?" + failmsg = ( + "{} (remote commit) != {} (local commit) for {}:{}. Did you try 'mepo push'?" + ) if remote_id != local_id: msg = failmsg.format(remote_id, local_id, comp_name, curr_ver_name) raise Exception(msg) diff --git a/src/mepo/command/stage/stage.py b/src/mepo/command/stage.py similarity index 78% rename from src/mepo/command/stage/stage.py rename to src/mepo/command/stage.py index 5f7d3d7..7a3b996 100644 --- a/src/mepo/command/stage/stage.py +++ b/src/mepo/command/stage.py @@ -1,7 +1,8 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository -from mepo.state.component import MepoVersion +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository +from ..component import MepoVersion + def run(args): allcomps = MepoState.read_state() @@ -11,9 +12,10 @@ def run(args): git = GitRepository(comp.remote, comp.local) stage_files(git, comp, args.untracked) + def stage_files(git, comp, untracked=False, commit=False): curr_ver = MepoVersion(*git.get_version()) - if curr_ver.detached: # detached head + if curr_ver.detached: # detached head raise Exception(f"{comp.name} has detached head! Cannot stage.") for myfile in git.get_changed_files(untracked=untracked): git.stage_file(myfile) @@ -22,4 +24,3 @@ def stage_files(git, comp, untracked=False, commit=False): print(f"Staged: {print_output}") else: print(f"+ {print_output}") - diff --git a/src/mepo/command/stash.py b/src/mepo/command/stash.py new file mode 100644 index 0000000..4da7932 --- /dev/null +++ b/src/mepo/command/stash.py @@ -0,0 +1,16 @@ +from .stash_list import run as stash_list_run +from .stash_pop import run as stash_pop_run +from .stash_apply import run as stash_apply_run +from .stash_push import run as stash_push_run +from .stash_show import run as stash_show_run + + +def run(args): + d = { + "list": stash_list_run, + "pop": stash_pop_run, + "apply": stash_apply_run, + "push": stash_push_run, + "show": stash_show_run, + } + d[args.mepo_stash_cmd](args) diff --git a/src/mepo/command/stash/stash.py b/src/mepo/command/stash/stash.py deleted file mode 100644 index fe0ef3e..0000000 --- a/src/mepo/command/stash/stash.py +++ /dev/null @@ -1,19 +0,0 @@ -import subprocess as sp - -from mepo.state.state import MepoState - -from mepo.command.stash.list import list -from mepo.command.stash.pop import pop -from mepo.command.stash.apply import apply -from mepo.command.stash.push import push -from mepo.command.stash.show import show - -def run(args): - d = { - 'list': list, - 'pop': pop, - 'apply': apply, - 'push': push, - 'show': show, - } - d[args.mepo_stash_cmd].run(args) diff --git a/src/mepo/command/stash/apply/apply.py b/src/mepo/command/stash_apply.py similarity index 63% rename from src/mepo/command/stash/apply/apply.py rename to src/mepo/command/stash_apply.py index 564b034..54617ce 100644 --- a/src/mepo/command/stash/apply/apply.py +++ b/src/mepo/command/stash_apply.py @@ -1,6 +1,7 @@ -from mepo.state.state import MepoState -from mepo.utilities import verify -from mepo.repository.git import GitRepository +from ..state import MepoState +from ..utilities import verify +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() @@ -9,4 +10,4 @@ def run(args): for comp in comps2appst: git = GitRepository(comp.remote, comp.local) git.apply_stash() - #print('+ {}'.format(comp.name)) + # print('+ {}'.format(comp.name)) diff --git a/src/mepo/command/stash/list/list.py b/src/mepo/command/stash_list.py similarity index 53% rename from src/mepo/command/stash/list/list.py rename to src/mepo/command/stash_list.py index d7f0510..a93537c 100644 --- a/src/mepo/command/stash/list/list.py +++ b/src/mepo/command/stash_list.py @@ -1,13 +1,14 @@ -from mepo.state.state import MepoState -from mepo.repository.git import GitRepository +from ..state import MepoState +from ..git import GitRepository + def run(args): allcomps = MepoState.read_state() max_namelen = len(max([x.name for x in allcomps], key=len)) - FMT = '{:<%s.%ss} | {: namedtuple + v = MepoVersion(*v) # * for arg unpacking + setattr(self, k, v) + return self + + def serialize(self): + d = {} + for k in self.__slots__: + v = getattr(self, k) + if k == "version": + # namedtuple -> list + v = list(v) + d.update({k: v}) + return d + + +def get_current_remote_url(): + cmd = "git remote get-url origin" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output + + +def decorate_node(item, flag, style): + # If we do not pass in a style... + if not style: + # Just use what's in components.yaml + return item + # else use the style + else: + item = item.replace(flag, "") + if style == "naked": + output = item + elif style == "prefix": + output = flag + item + elif style == "postfix": + output = item + flag + return output + + +# From https://learning.oreilly.com/library/view/python-cookbook/0596001673/ch04s16.html +def splitall(path): + allparts = [] + while 1: + parts = os.path.split(path) + if parts[0] == path: # sentinel for absolute paths + allparts.insert(0, parts[0]) + break + elif parts[1] == path: # sentinel for relative paths + allparts.insert(0, parts[1]) + break + else: + path = parts[0] + allparts.insert(0, parts[1]) + return allparts diff --git a/src/mepo/config/config_file.py b/src/mepo/config/config_file.py deleted file mode 100644 index b4c1089..0000000 --- a/src/mepo/config/config_file.py +++ /dev/null @@ -1,55 +0,0 @@ -import pathlib -import yaml - -from mepo.state.exceptions import SuffixNotRecognizedError - -# From https://github.com/yaml/pyyaml/issues/127#issuecomment-525800484 -class AddBlankLinesDumper(yaml.SafeDumper): - # HACK: insert blank lines between top-level objects - # inspired by https://stackoverflow.com/a/44284819/3786245 - def write_line_break(self, data=None): - super().write_line_break(data) - - if len(self.indents) == 1: - super().write_line_break() - -class ConfigFile(object): - - __slots__ = ['__filename', '__filetype'] - - def __init__(self, filename): - self.__filename = filename - SUFFIX_LIST = ['.yaml', '.json', '.cfg'] - file_suffix = pathlib.Path(filename).suffix - if file_suffix in SUFFIX_LIST: - self.__filetype = file_suffix[1:] - else: - raise SuffixNotRecognizedError('suffix {} not supported'.format(file_suffix)) - - def read_file(self): - '''Call read_yaml, read_json etc. using dispatch pattern''' - return getattr(self, 'read_'+self.__filetype)() - - def read_yaml(self): - '''Read yaml config file and return a dict containing contents''' - import yaml - with open(self.__filename, 'r') as fin: - d = yaml.safe_load(fin) - return d - - def read_json(self): - '''Read json config file and return a dict containing contents''' - import json - with open(self.__filename, 'r') as fin: - d = json.load(fin) - return d - - def read_cfg(self): - '''Read python config file and return a dict containing contents''' - raise NotImplementedError('Reading of cfg file has not yet been implemented') - - def write_yaml(self, d): - '''Dump dict d into a yaml file''' - import yaml - with open(self.__filename, 'w') as fout: - yaml.dump(d, fout, sort_keys = False, Dumper=AddBlankLinesDumper) diff --git a/src/mepo/git.py b/src/mepo/git.py new file mode 100644 index 0000000..4518a54 --- /dev/null +++ b/src/mepo/git.py @@ -0,0 +1,579 @@ +import os +import shutil +import shlex +import subprocess as sp + +from urllib.parse import urljoin + +from .state import MepoState +from .utilities import shellcmd +from .utilities import colors +from .utilities.exceptions import RepoAlreadyClonedError + + +def get_editor(): + """ + Return GIT_EDITOR + """ + result = sp.run( + "git var GIT_EDITOR".split(), stdout=sp.PIPE, stderr=sp.PIPE, check=True + ) + return result.stdout.rstrip().decode("utf-8") # byte to utf-8 + + +class GitRepository: + """ + Class to consolidate git commands + """ + + __slots__ = ["__local", "__full_local_path", "__remote", "__git"] + + def __init__(self, remote_url, local_path): + self.__local = local_path + + if remote_url.startswith(".."): + rel_remote = os.path.basename(remote_url) + fixture_url = get_current_remote_url() + self.__remote = urljoin(fixture_url, rel_remote) + else: + self.__remote = remote_url + + root_dir = MepoState.get_root_dir() + full_local_path = os.path.normpath(os.path.join(root_dir, local_path)) + self.__full_local_path = full_local_path + self.__git = 'git -C "{}"'.format(self.__full_local_path) + + def get_local_path(self): + return self.__local + + def get_full_local_path(self): + return self.__full_local_path + + def get_remote_url(self): + return self.__remote + + def clone(self, version, recurse, type, comp_name, partial=None): + cmd1 = "git clone " + + if partial == "blobless": + cmd1 += "--filter=blob:none " + elif partial == "treeless": + cmd1 += "--filter=tree:0 " + + if recurse: + cmd1 += "--recurse-submodules " + + cmd1 += "--quiet {} {}".format(self.__remote, self.__full_local_path) + try: + shellcmd.run(shlex.split(cmd1)) + except sp.CalledProcessError: + raise RepoAlreadyClonedError(f"Error! Repo [{comp_name}] already cloned") + + cmd2 = "git -C {} checkout {}".format(self.__full_local_path, version) + shellcmd.run(shlex.split(cmd2)) + cmd3 = "git -C {} checkout --detach".format(self.__full_local_path) + shellcmd.run(shlex.split(cmd3)) + + # NOTE: The above looks odd because of a quirk of git. You can't do + # git checkout --detach branch unless the branch is local. But + # since this is at clone time, all branches are remote. Thus, + # we have to do a git checkout branch and then detach. + + def checkout(self, version, detach=False): + cmd = self.__git + " checkout " + cmd += "--quiet {}".format(version) + shellcmd.run(shlex.split(cmd)) + if detach: + cmd2 = self.__git + " checkout --detach" + shellcmd.run(shlex.split(cmd2)) + + def sparsify(self, sparse_config): + dst = os.path.join(self.__full_local_path, ".git", "info", "sparse-checkout") + os.makedirs(os.path.dirname(dst), exist_ok=True) + shutil.copy(sparse_config, dst) + cmd1 = self.__git + " config core.sparseCheckout true" + shellcmd.run(shlex.split(cmd1)) + cmd2 = self.__git + " read-tree -mu HEAD" + shellcmd.run(shlex.split(cmd2)) + + def list_branch(self, all=False, nocolor=False): + cmd = self.__git + " branch" + if all: + cmd += " -a" + if nocolor: + cmd += " --color=never" + return shellcmd.run(shlex.split(cmd), output=True) + + def list_tags(self): + cmd = self.__git + " tag" + return shellcmd.run(shlex.split(cmd), output=True) + + def rev_list(self, tag): + cmd = self.__git + " rev-list -n 1 {}".format(tag) + return shellcmd.run(shlex.split(cmd), output=True) + + def rev_parse(self, short=False): + cmd = self.__git + " rev-parse --verify HEAD" + if short: + cmd += " --short" + return shellcmd.run(shlex.split(cmd), output=True) + + def list_stash(self): + cmd = self.__git + " stash list" + return shellcmd.run(shlex.split(cmd), output=True) + + def pop_stash(self): + cmd = self.__git + " stash pop" + return shellcmd.run(shlex.split(cmd), output=True) + + def apply_stash(self): + cmd = self.__git + " stash apply" + return shellcmd.run(shlex.split(cmd), output=True) + + def push_stash(self, message): + cmd = self.__git + " stash push" + if message: + cmd += " -m {}".format(message) + return shellcmd.run(shlex.split(cmd), output=True) + + def show_stash(self, patch): + cmd = self.__git + " stash show" + if patch: + cmd += " -p --color" + output = shellcmd.run(shlex.split(cmd), output=True) + return output.rstrip() + + def run_diff(self, args=None, ignore_submodules=False): + cmd = "git -C {}".format(self.__full_local_path) + if args.ignore_permissions: + cmd += " -c core.fileMode=false" + cmd += " diff --color" + if args.name_only: + cmd += " --name-only" + if args.name_status: + cmd += " --name-status" + if args.staged: + cmd += " --staged" + if args.ignore_space_change: + cmd += " --ignore-space-change" + if ignore_submodules: + cmd += " --ignore-submodules=all" + output = shellcmd.run(shlex.split(cmd), output=True) + return output.rstrip() + + def fetch(self, args=None): + cmd = self.__git + " fetch" + if args.all: + cmd += " --all" + if args.prune: + cmd += " --prune" + if args.tags: + cmd += " --tags" + if args.force: + cmd += " --force" + return shellcmd.run(shlex.split(cmd), output=True) + + def create_branch(self, branch_name): + cmd = self.__git + " branch {}".format(branch_name) + shellcmd.run(shlex.split(cmd)) + + def create_tag(self, tag_name, annotate, message, tf_file=None): + if annotate: + if tf_file: + cmd = [ + "git", + "-C", + self.__full_local_path, + "tag", + "-a", + "-F", + tf_file, + tag_name, + ] + elif message: + cmd = [ + "git", + "-C", + self.__full_local_path, + "tag", + "-a", + "-m", + message, + tag_name, + ] + else: + raise Exception("This should not happen") + else: + cmd = ["git", "-C", self.__full_local_path, "tag", tag_name] + shellcmd.run(cmd) + + def delete_branch(self, branch_name, force): + delete = "-d" + if force: + delete = "-D" + cmd = self.__git + " branch {} {}".format(delete, branch_name) + shellcmd.run(shlex.split(cmd)) + + def delete_tag(self, tag_name): + cmd = self.__git + " tag -d {}".format(tag_name) + shellcmd.run(shlex.split(cmd)) + + def push_tag(self, tag_name, force, delete): + cmd = self.__git + " push" + if force: + cmd += " --force" + if delete: + cmd += " --delete" + cmd += " origin {}".format(tag_name) + shellcmd.run(shlex.split(cmd)) + + def verify_branch_or_tag(self, ref_name): + branch_cmd = self.__git + f" show-branch remotes/origin/{ref_name}" + tag_cmd = self.__git + f" rev-parse {ref_name}" + branch_status = shellcmd.run(shlex.split(branch_cmd), status=True) + ref_type = "UNKNOWN" + if branch_status != 0: + status = shellcmd.run(shlex.split(tag_cmd), status=True) + ref_type = "Tag" + else: + status = branch_status + ref_type = "Branch" + return status, ref_type + + def check_status(self, ignore_permissions=False, ignore_submodules=False): + cmd = "git -C {}".format(self.__full_local_path) + if ignore_permissions: + cmd += " -c core.fileMode=false" + cmd += " status --porcelain=v2" + if ignore_submodules: + cmd += " --ignore-submodules=all" + output = shellcmd.run(shlex.split(cmd), output=True) + if output.strip(): + output_list = output.splitlines() + + # Grab the file names first for pretty printing + file_name_list = [item.split()[-1] for item in output_list] + max_file_name_length = len(max(file_name_list, key=len)) + + verbose_output_list = [] + for item in output_list: + + index_field = item.split()[0] + if index_field == "2": + new_file_name = colors.YELLOW + item.split()[-2] + colors.RESET + + file_name = item.split()[-1] + + short_status = item.split()[1] + + if index_field == "?": + verbose_status = colors.RED + "untracked file" + colors.RESET + + elif short_status == ".D": + verbose_status = colors.RED + "deleted, not staged" + colors.RESET + elif short_status == ".M": + verbose_status = colors.RED + "modified, not staged" + colors.RESET + elif short_status == ".A": + verbose_status = colors.RED + "added, not staged" + colors.RESET + elif short_status == ".T": + verbose_status = ( + colors.RED + "typechange, not staged" + colors.RESET + ) + + elif short_status == "D.": + verbose_status = colors.GREEN + "deleted, staged" + colors.RESET + elif short_status == "M.": + verbose_status = colors.GREEN + "modified, staged" + colors.RESET + elif short_status == "A.": + verbose_status = colors.GREEN + "added, staged" + colors.RESET + elif short_status == "T.": + verbose_status = colors.GREEN + "typechange, staged" + colors.RESET + + elif short_status == "MM": + verbose_status = ( + colors.GREEN + + "modified, staged" + + colors.RESET + + " with " + + colors.RED + + "unstaged changes" + + colors.RESET + ) + elif short_status == "MD": + verbose_status = ( + colors.GREEN + + "modified, staged" + + colors.RESET + + " but " + + colors.RED + + "deleted, not staged" + + colors.RESET + ) + + elif short_status == "AM": + verbose_status = ( + colors.GREEN + + "added, staged" + + colors.RESET + + " with " + + colors.RED + + "unstaged changes" + + colors.RESET + ) + elif short_status == "AD": + verbose_status = ( + colors.GREEN + + "added, staged" + + colors.RESET + + " but " + + colors.RED + + "deleted, not staged" + + colors.RESET + ) + + elif short_status == "TM": + verbose_status = ( + colors.GREEN + + "typechange, staged" + + colors.RESET + + " with " + + colors.RED + + "unstaged changes" + + colors.RESET + ) + elif short_status == "TD": + verbose_status = ( + colors.GREEN + + "typechange, staged" + + colors.RESET + + " but " + + colors.RED + + "deleted, not staged" + + colors.RESET + ) + + elif short_status == "R.": + verbose_status = ( + colors.GREEN + + "renamed" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + ) + elif short_status == "RM": + verbose_status = ( + colors.GREEN + + "renamed, staged" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + + " with " + + colors.RED + + "unstaged changes" + + colors.RESET + ) + elif short_status == "RD": + verbose_status = ( + colors.GREEN + + "renamed, staged" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + + " but " + + colors.RED + + "deleted, not staged" + + colors.RESET + ) + + elif short_status == "C.": + verbose_status = ( + colors.GREEN + + "copied" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + ) + elif short_status == "CM": + verbose_status = ( + colors.GREEN + + "copied, staged" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + + " with " + + colors.RED + + "unstaged changes" + + colors.RESET + ) + elif short_status == "CD": + verbose_status = ( + colors.GREEN + + "copied, staged" + + colors.RESET + + " as " + + colors.YELLOW + + new_file_name + + colors.RESET + + " but " + + colors.RED + + "deleted, not staged" + + colors.RESET + ) + + else: + verbose_status = ( + colors.CYAN + + "unknown" + + colors.RESET + + " (please contact mepo maintainer)" + ) + + verbose_status_string = ( + "{file_name:>{file_name_length}}: {verbose_status}".format( + file_name=file_name, + file_name_length=max_file_name_length, + verbose_status=verbose_status, + ) + ) + verbose_output_list.append(verbose_status_string) + + output = "\n".join(verbose_output_list) + + return output.rstrip() + + def __get_modified_files(self, orig_ver, comp_type): + if not orig_ver: + cmd = self.__git + " diff --name-only" + else: + if comp_type == "b": + cmd = self.__git + " diff --name-only origin/{}".format(orig_ver) + else: + cmd = self.__git + " diff --name-only {}".format(orig_ver) + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output.split("\n") if output else [] + + def __get_untracked_files(self): + cmd = self.__git + " ls-files --others --exclude-standard" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output.split("\n") if output else [] + + def get_changed_files(self, untracked=False, orig_ver=None, comp_type=None): + changed_files = self.__get_modified_files(orig_ver, comp_type) + if untracked: + changed_files += self.__get_untracked_files() + return changed_files + + def stage_file(self, myfile): + cmd = self.__git + " add {}".format(myfile) + shellcmd.run(shlex.split(cmd)) + + def get_staged_files(self): + cmd = self.__git + " diff --name-only --staged" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output.split("\n") if output else [] + + def unstage_file(self, myfile): + cmd = self.__git + " reset -- {}".format(myfile) + shellcmd.run(shlex.split(cmd)) + + def commit_files(self, message, tf_file=None): + if tf_file: + cmd = ["git", "-C", self.__full_local_path, "commit", "-F", tf_file] + elif message: + cmd = ["git", "-C", self.__full_local_path, "commit", "-m", message] + else: + raise Exception("This should not happen") + shellcmd.run(cmd) + + def push(self): + cmd = self.__git + " push -u {}".format(self.__remote) + return shellcmd.run(shlex.split(cmd), output=True).strip() + + def get_remote_latest_commit_id(self, branch, commit_type): + if commit_type == "h": + cmd = self.__git + " cat-file -e {}".format(branch) + status = shellcmd.run(shlex.split(cmd), status=True) + if status != 0: + msg = "Hash {} does not exist on {}".format(branch, self.__remote) + msg += " Have you run 'mepo push'?" + raise RuntimeError(msg) + return branch + else: + # If we are a branch... + if commit_type == "b": + msgtype = "Branch" + reftype = "heads" + elif commit_type == "t": + msgtype = "Tag" + reftype = "tags" + else: + raise RuntimeError("Should not get here") + cmd = self.__git + " ls-remote {} refs/{}/{}".format( + self.__remote, reftype, branch + ) + output = shellcmd.run(shlex.split(cmd), stdout=True).strip() + if not output: + # msg = '{} {} does not exist on {}'.format(msgtype, branch, self.__remote) + # msg += " Have you run 'mepo push'?" + # raise RuntimeError(msg) + cmd = self.__git + " rev-parse HEAD" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output.split()[0] + + def get_local_latest_commit_id(self): + cmd = self.__git + " rev-parse HEAD" + return shellcmd.run(shlex.split(cmd), output=True).strip() + + def pull(self): + cmd = self.__git + " pull" + return shellcmd.run(shlex.split(cmd), output=True).strip() + + def get_version(self): + cmd = self.__git + " show -s --pretty=%D HEAD" + output = shellcmd.run(shlex.split(cmd), output=True) + if output.startswith("HEAD ->"): # an actual branch + detached = False + name = output.split(",")[0].split("->")[1].strip() + tYpe = "b" + elif output.startswith("HEAD,"): # detached head + detached = True + tmp = output.split(",")[1].strip() + if tmp.startswith("tag:"): # tag + name = tmp[5:] + tYpe = "t" + else: + # This was needed for when we weren't explicitly detaching on clone + # cmd_for_branch = self.__git + ' reflog HEAD -n 1' + # reflog_output = shellcmd.run(shlex.split(cmd_for_branch), output=True) + # name = reflog_output.split()[-1].strip() + name = output.split()[-1].strip() + tYpe = "b" + elif output.startswith("HEAD"): # Assume hash + cmd = self.__git + " rev-parse HEAD" + hash_out = shellcmd.run(shlex.split(cmd), output=True) + detached = True + name = hash_out.rstrip() + tYpe = "h" + elif output.startswith("grafted"): + cmd = self.__git + " describe --always" + hash_out = shellcmd.run(shlex.split(cmd), output=True) + detached = True + name = hash_out.rstrip() + tYpe = "h" + return (name, tYpe, detached) + + +def get_current_remote_url(): + cmd = "git remote get-url origin" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output diff --git a/src/mepo/main.py b/src/mepo/main.py deleted file mode 100644 index 30a3135..0000000 --- a/src/mepo/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from mepo.cmdline.parser import MepoArgParser -from mepo.command import command - -def main(): - args = MepoArgParser().parse() - command.run(args) diff --git a/src/mepo/registry.py b/src/mepo/registry.py new file mode 100644 index 0000000..e5eb9cf --- /dev/null +++ b/src/mepo/registry.py @@ -0,0 +1,80 @@ +import yaml +import pathlib + +from .utilities.exceptions import SuffixNotRecognizedError + + +# From https://github.com/yaml/pyyaml/issues/127#issuecomment-525800484 +class AddBlankLinesDumper(yaml.SafeDumper): + # HACK: insert blank lines between top-level objects + # inspired by https://stackoverflow.com/a/44284819/3786245 + def write_line_break(self, data=None): + super().write_line_break(data) + + if len(self.indents) == 1: + super().write_line_break() + + +class Registry(object): + + __slots__ = ["__filename", "__filetype"] + + def __init__(self, filename): + self.__filename = filename + SUFFIX_LIST = [".yaml", ".json", ".cfg"] + file_suffix = pathlib.Path(filename).suffix + if file_suffix in SUFFIX_LIST: + self.__filetype = file_suffix[1:] + else: + raise SuffixNotRecognizedError( + "suffix {} not supported".format(file_suffix) + ) + + def __validate(self, d): + git_tag_types = {"branch", "tag", "hash"} + num_fixtures = 0 + for k, v in d.items(): + if "fixture" in v: + # In case of a fixture, develop is the only additional key + num_fixtures += 1 + assert list(v.keys()) == ["fixture", "develop"] + else: + # For non-fixture, one and only one of branch/tag/hash allowed + xsection = git_tag_types.intersection(set(v.keys())) + if len(xsection) != 1: + raise ValueError(f"{k} needs one and only one of {git_tag_types}") + # Can have one and only one fixture + assert num_fixtures == 1 + + def read_file(self): + """Call read_yaml, read_json etc. using dispatch pattern""" + return getattr(self, "read_" + self.__filetype)() + + def read_yaml(self): + """Read yaml registry and return a dict containing contents""" + import yaml + + with open(self.__filename, "r") as fin: + d = yaml.safe_load(fin) + self.__validate(d) + return d + + def read_json(self): + """Read json registry and return a dict containing contents""" + import json + + with open(self.__filename, "r") as fin: + d = json.load(fin) + self.__validate(d) + return d + + def read_cfg(self): + """Read python registry and return a dict containing contents""" + raise NotImplementedError("Reading of cfg file has not yet been implemented") + + def write_yaml(self, d): + """Dump dict d into a yaml file""" + import yaml + + with open(self.__filename, "w") as fout: + yaml.dump(d, fout, sort_keys=False, Dumper=AddBlankLinesDumper) diff --git a/src/mepo/repository/git.py b/src/mepo/repository/git.py deleted file mode 100644 index e3a566a..0000000 --- a/src/mepo/repository/git.py +++ /dev/null @@ -1,421 +0,0 @@ -import os -import shutil -import subprocess -import shlex - -from urllib.parse import urljoin - -from mepo.state.state import MepoState -from mepo.utilities import shellcmd -from mepo.utilities import colors -from mepo.state.exceptions import RepoAlreadyClonedError - -class GitRepository(object): - """ - Class to consolidate git commands - """ - __slots__ = ['__local', '__full_local_path', '__remote', '__git'] - - def __init__(self, remote_url, local_path): - self.__local = local_path - - if remote_url.startswith('..'): - rel_remote = os.path.basename(remote_url) - fixture_url = get_current_remote_url() - self.__remote = urljoin(fixture_url,rel_remote) - else: - self.__remote = remote_url - - root_dir = MepoState.get_root_dir() - full_local_path=os.path.normpath(os.path.join(root_dir,local_path)) - self.__full_local_path=full_local_path - self.__git = 'git -C "{}"'.format(self.__full_local_path) - - def get_local_path(self): - return self.__local - - def get_full_local_path(self): - return self.__full_local_path - - def get_remote_url(self): - return self.__remote - - def clone(self, version, recurse, type, comp_name, partial=None): - cmd1 = 'git clone ' - - if partial == 'blobless': - cmd1 += '--filter=blob:none ' - elif partial == 'treeless': - cmd1 += '--filter=tree:0 ' - - if recurse: - cmd1 += '--recurse-submodules ' - - cmd1 += '--quiet {} {}'.format(self.__remote, self.__full_local_path) - try: - shellcmd.run(shlex.split(cmd1)) - except subprocess.CalledProcessError: - raise RepoAlreadyClonedError(f'Error! Repo [{comp_name}] already cloned') - - cmd2 = 'git -C {} checkout {}'.format(self.__full_local_path, version) - shellcmd.run(shlex.split(cmd2)) - cmd3 = 'git -C {} checkout --detach'.format(self.__full_local_path) - shellcmd.run(shlex.split(cmd3)) - - # NOTE: The above looks odd because of a quirk of git. You can't do - # git checkout --detach branch unless the branch is local. But - # since this is at clone time, all branches are remote. Thus, - # we have to do a git checkout branch and then detach. - - def checkout(self, version, detach=False): - cmd = self.__git + ' checkout ' - cmd += '--quiet {}'.format(version) - shellcmd.run(shlex.split(cmd)) - if detach: - cmd2 = self.__git + ' checkout --detach' - shellcmd.run(shlex.split(cmd2)) - - def sparsify(self, sparse_config): - dst = os.path.join(self.__full_local_path, '.git', 'info', 'sparse-checkout') - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.copy(sparse_config, dst) - cmd1 = self.__git + ' config core.sparseCheckout true' - shellcmd.run(shlex.split(cmd1)) - cmd2 = self.__git + ' read-tree -mu HEAD' - shellcmd.run(shlex.split(cmd2)) - - def list_branch(self, all=False): - cmd = self.__git + ' branch' - if all: - cmd += ' -a' - return shellcmd.run(shlex.split(cmd), output=True) - - def list_tags(self): - cmd = self.__git + ' tag' - return shellcmd.run(shlex.split(cmd), output=True) - - def rev_list(self, tag): - cmd = self.__git + ' rev-list -n 1 {}'.format(tag) - return shellcmd.run(shlex.split(cmd), output=True) - - def rev_parse(self, short=False): - cmd = self.__git + ' rev-parse --verify HEAD' - if short: - cmd += ' --short' - return shellcmd.run(shlex.split(cmd), output=True) - - def list_stash(self): - cmd = self.__git + ' stash list' - return shellcmd.run(shlex.split(cmd), output=True) - - def pop_stash(self): - cmd = self.__git + ' stash pop' - return shellcmd.run(shlex.split(cmd), output=True) - - def apply_stash(self): - cmd = self.__git + ' stash apply' - return shellcmd.run(shlex.split(cmd), output=True) - - def push_stash(self, message): - cmd = self.__git + ' stash push' - if message: - cmd += ' -m {}'.format(message) - return shellcmd.run(shlex.split(cmd), output=True) - - def show_stash(self, patch): - cmd = self.__git + ' stash show' - if patch: - cmd += ' -p --color' - output = shellcmd.run(shlex.split(cmd),output=True) - return output.rstrip() - - def run_diff(self, args=None, ignore_submodules=False): - cmd = 'git -C {}'.format(self.__full_local_path) - if args.ignore_permissions: - cmd += ' -c core.fileMode=false' - cmd += ' diff --color' - if args.name_only: - cmd += ' --name-only' - if args.name_status: - cmd += ' --name-status' - if args.staged: - cmd += ' --staged' - if args.ignore_space_change: - cmd += ' --ignore-space-change' - if ignore_submodules: - cmd += ' --ignore-submodules=all' - output = shellcmd.run(shlex.split(cmd),output=True) - return output.rstrip() - - def fetch(self, args=None): - cmd = self.__git + ' fetch' - if args.all: - cmd += ' --all' - if args.prune: - cmd += ' --prune' - if args.tags: - cmd += ' --tags' - if args.force: - cmd += ' --force' - return shellcmd.run(shlex.split(cmd), output=True) - - def create_branch(self, branch_name): - cmd = self.__git + ' branch {}'.format(branch_name) - shellcmd.run(shlex.split(cmd)) - - def create_tag(self, tag_name, annotate, message, tf_file=None): - if annotate: - if tf_file: - cmd = ['git', '-C', self.__full_local_path, 'tag', '-a', '-F', tf_file, tag_name] - elif message: - cmd = ['git', '-C', self.__full_local_path, 'tag', '-a', '-m', message, tag_name] - else: - raise Exception("This should not happen") - else: - cmd = ['git', '-C', self.__full_local_path, 'tag', tag_name] - shellcmd.run(cmd) - - def delete_branch(self, branch_name, force): - delete = '-d' - if force: - delete = '-D' - cmd = self.__git + ' branch {} {}'.format(delete, branch_name) - shellcmd.run(shlex.split(cmd)) - - def delete_tag(self, tag_name): - cmd = self.__git + ' tag -d {}'.format(tag_name) - shellcmd.run(shlex.split(cmd)) - - def push_tag(self, tag_name, force, delete): - cmd = self.__git + ' push' - if force: - cmd += ' --force' - if delete: - cmd += ' --delete' - cmd += ' origin {}'.format(tag_name) - shellcmd.run(shlex.split(cmd)) - - def verify_branch_or_tag(self, ref_name): - branch_cmd = self.__git + f' show-branch remotes/origin/{ref_name}' - tag_cmd = self.__git + f' rev-parse {ref_name}' - branch_status = shellcmd.run(shlex.split(branch_cmd),status=True) - ref_type = "UNKNOWN" - if branch_status != 0: - status = shellcmd.run(shlex.split(tag_cmd),status=True) - ref_type = "Tag" - else: - status = branch_status - ref_type = "Branch" - return status, ref_type - - def check_status(self, ignore_permissions=False, ignore_submodules=False): - cmd = 'git -C {}'.format(self.__full_local_path) - if ignore_permissions: - cmd += ' -c core.fileMode=false' - cmd += ' status --porcelain=v2' - if ignore_submodules: - cmd += ' --ignore-submodules=all' - output = shellcmd.run(shlex.split(cmd), output=True) - if output.strip(): - output_list = output.splitlines() - - # Grab the file names first for pretty printing - file_name_list = [item.split()[-1] for item in output_list] - max_file_name_length = len(max(file_name_list, key=len)) - - verbose_output_list = [] - for item in output_list: - - index_field = item.split()[0] - if index_field == "2": - new_file_name = colors.YELLOW + item.split()[-2] + colors.RESET - - file_name = item.split()[-1] - - short_status = item.split()[1] - - if index_field == "?": - verbose_status = colors.RED + "untracked file" + colors.RESET - - elif short_status == ".D": - verbose_status = colors.RED + "deleted, not staged" + colors.RESET - elif short_status == ".M": - verbose_status = colors.RED + "modified, not staged" + colors.RESET - elif short_status == ".A": - verbose_status = colors.RED + "added, not staged" + colors.RESET - elif short_status == ".T": - verbose_status = colors.RED + "typechange, not staged" + colors.RESET - - elif short_status == "D.": - verbose_status = colors.GREEN + "deleted, staged" + colors.RESET - elif short_status == "M.": - verbose_status = colors.GREEN + "modified, staged" + colors.RESET - elif short_status == "A.": - verbose_status = colors.GREEN + "added, staged" + colors.RESET - elif short_status == "T.": - verbose_status = colors.GREEN + "typechange, staged" + colors.RESET - - elif short_status == "MM": - verbose_status = colors.GREEN + "modified, staged" + colors.RESET + " with " + colors.RED + "unstaged changes" + colors.RESET - elif short_status == "MD": - verbose_status = colors.GREEN + "modified, staged" + colors.RESET + " but " + colors.RED + "deleted, not staged" + colors.RESET - - elif short_status == "AM": - verbose_status = colors.GREEN + "added, staged" + colors.RESET + " with " + colors.RED + "unstaged changes" + colors.RESET - elif short_status == "AD": - verbose_status = colors.GREEN + "added, staged" + colors.RESET + " but " + colors.RED + "deleted, not staged" + colors.RESET - - elif short_status == "TM": - verbose_status = colors.GREEN + "typechange, staged" + colors.RESET + " with " + colors.RED + "unstaged changes" + colors.RESET - elif short_status == "TD": - verbose_status = colors.GREEN + "typechange, staged" + colors.RESET + " but " + colors.RED + "deleted, not staged" + colors.RESET - - elif short_status == "R.": - verbose_status = colors.GREEN + "renamed" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET - elif short_status == "RM": - verbose_status = colors.GREEN + "renamed, staged" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET + " with " + colors.RED + "unstaged changes" + colors.RESET - elif short_status == "RD": - verbose_status = colors.GREEN + "renamed, staged" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET + " but " + colors.RED + "deleted, not staged" + colors.RESET - - elif short_status == "C.": - verbose_status = colors.GREEN + "copied" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET - elif short_status == "CM": - verbose_status = colors.GREEN + "copied, staged" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET + " with " + colors.RED + "unstaged changes" + colors.RESET - elif short_status == "CD": - verbose_status = colors.GREEN + "copied, staged" + colors.RESET + " as " + colors.YELLOW + new_file_name + colors.RESET + " but " + colors.RED + "deleted, not staged" + colors.RESET - - else: - verbose_status = colors.CYAN + "unknown" + colors.RESET + " (please contact mepo maintainer)" - - verbose_status_string = "{file_name:>{file_name_length}}: {verbose_status}".format( - file_name=file_name, file_name_length=max_file_name_length, - verbose_status=verbose_status) - verbose_output_list.append(verbose_status_string) - - output = "\n".join(verbose_output_list) - - return output.rstrip() - - def __get_modified_files(self, orig_ver, comp_type): - if not orig_ver: - cmd = self.__git + ' diff --name-only' - else: - if comp_type == "b": - cmd = self.__git + ' diff --name-only origin/{}'.format(orig_ver) - else: - cmd = self.__git + ' diff --name-only {}'.format(orig_ver) - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output.split('\n') if output else [] - - def __get_untracked_files(self): - cmd = self.__git + ' ls-files --others --exclude-standard' - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output.split('\n') if output else [] - - def get_changed_files(self, untracked=False, orig_ver=None, comp_type=None): - changed_files = self.__get_modified_files(orig_ver, comp_type) - if untracked: - changed_files += self.__get_untracked_files() - return changed_files - - def stage_file(self, myfile): - cmd = self.__git + ' add {}'.format(myfile) - shellcmd.run(shlex.split(cmd)) - - def get_staged_files(self): - cmd = self.__git + ' diff --name-only --staged' - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output.split('\n') if output else [] - - def unstage_file(self, myfile): - cmd = self.__git + ' reset -- {}'.format(myfile) - shellcmd.run(shlex.split(cmd)) - - def commit_files(self, message, tf_file=None): - if tf_file: - cmd = ['git', '-C', self.__full_local_path, 'commit', '-F', tf_file] - elif message: - cmd = ['git', '-C', self.__full_local_path, 'commit', '-m', message] - else: - raise Exception("This should not happen") - shellcmd.run(cmd) - - def push(self): - cmd = self.__git + ' push -u {}'.format(self.__remote) - return shellcmd.run(shlex.split(cmd), output=True).strip() - - def get_remote_latest_commit_id(self, branch, commit_type): - if commit_type == 'h': - cmd = self.__git + ' cat-file -e {}'.format(branch) - status = shellcmd.run(shlex.split(cmd), status=True) - if status != 0: - msg = 'Hash {} does not exist on {}'.format(branch, self.__remote) - msg += " Have you run 'mepo push'?" - raise RuntimeError(msg) - return branch - else: - # If we are a branch... - if commit_type == 'b': - msgtype = "Branch" - reftype = 'heads' - elif commit_type == 't': - msgtype = 'Tag' - reftype = 'tags' - else: - raise RuntimeError("Should not get here") - cmd = self.__git + ' ls-remote {} refs/{}/{}'.format(self.__remote, reftype, branch) - output = shellcmd.run(shlex.split(cmd), stdout=True).strip() - if not output: - #msg = '{} {} does not exist on {}'.format(msgtype, branch, self.__remote) - #msg += " Have you run 'mepo push'?" - #raise RuntimeError(msg) - cmd = self.__git + ' rev-parse HEAD' - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output.split()[0] - - def get_local_latest_commit_id(self): - cmd = self.__git + ' rev-parse HEAD' - return shellcmd.run(shlex.split(cmd), output=True).strip() - - def pull(self): - cmd = self.__git + ' pull' - return shellcmd.run(shlex.split(cmd), output=True).strip() - - def get_version(self): - cmd = self.__git + ' show -s --pretty=%D HEAD' - output = shellcmd.run(shlex.split(cmd), output=True) - if output.startswith('HEAD ->'): # an actual branch - detached = False - name = output.split(',')[0].split('->')[1].strip() - tYpe = 'b' - elif output.startswith('HEAD,'): # detached head - detached = True - tmp = output.split(',')[1].strip() - if tmp.startswith('tag:'): # tag - name = tmp[5:] - tYpe = 't' - else: - # This was needed for when we weren't explicitly detaching on clone - #cmd_for_branch = self.__git + ' reflog HEAD -n 1' - #reflog_output = shellcmd.run(shlex.split(cmd_for_branch), output=True) - #name = reflog_output.split()[-1].strip() - name = output.split()[-1].strip() - tYpe = 'b' - elif output.startswith('HEAD'): # Assume hash - cmd = self.__git + ' rev-parse HEAD' - hash_out = shellcmd.run(shlex.split(cmd), output=True) - detached = True - name = hash_out.rstrip() - tYpe = 'h' - elif output.startswith('grafted'): - cmd = self.__git + ' describe --always' - hash_out = shellcmd.run(shlex.split(cmd), output=True) - detached = True - name = hash_out.rstrip() - tYpe = 'h' - return (name, tYpe, detached) - -def get_current_remote_url(): - cmd = 'git remote get-url origin' - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output diff --git a/src/mepo/state.py b/src/mepo/state.py new file mode 100644 index 0000000..827c10e --- /dev/null +++ b/src/mepo/state.py @@ -0,0 +1,168 @@ +import os +import sys +import json +import glob +import stat +import pickle + +from pathlib import Path + +from .registry import Registry +from .component import MepoComponent +from .utilities import shellcmd +from .utilities import colors +from .utilities.exceptions import StateDoesNotExistError +from .utilities.exceptions import StateAlreadyInitializedError +from .utilities.chdir import chdir as mepo_chdir +from .utilities.version import MepoVersion + + +class MepoState(object): + + __state_dir_name = ".mepo" + __state_fileptr_name = "state.json" + __state_fileptr_name_old = "state.pkl" + + @staticmethod + def get_parent_dirs(): + mypath = os.getcwd() + parentdirs = [mypath] + while mypath != "/": + mypath = os.path.dirname(mypath) + parentdirs.append(mypath) + return parentdirs + + @classmethod + def get_dir(cls): + """Return location of mepo state dir""" + for mydir in cls.get_parent_dirs(): + state_dir = os.path.join(mydir, cls.__state_dir_name) + if os.path.exists(state_dir): + return state_dir + raise OSError("mepo state dir [.mepo] does not exist") + + @classmethod + def get_root_dir(cls): + """Return fixture (root) directory that contains mepo state dir""" + return os.path.dirname(cls.get_dir()) + + @classmethod + def get_file(cls, old_style=False): + """Return location of mepo state file""" + if old_style: + fileptr_name = cls.__state_fileptr_name_old + else: + fileptr_name = cls.__state_fileptr_name + state_file = os.path.join(cls.get_dir(), fileptr_name) + if os.path.exists(state_file): + return state_file + else: + raise OSError(f"mepo state file [{state_file}] does not exist") + + @classmethod + def state_exists(cls, old_style=False): + try: + cls.get_file(old_style) + return True + except OSError: + return False + + @classmethod + def initialize(cls, project_registry, directory_style): + if cls.state_exists(): + raise StateAlreadyInitializedError("Error! mepo state already exists") + input_components = Registry(project_registry).read_file() + complist = list() + for name, comp in input_components.items(): + complist.append( + MepoComponent().registry_to_component(name, comp, directory_style) + ) + cls.write_state(complist) + + @staticmethod + def __mepo1_patch(): + """ + mepo1 to mepo2 includes renaming of directories + Since pickle requires that "the class definition must be importable + and live in the same module as when the object was stored", we need to + patch sys.modules to be able to read mepo1 state + """ + import mepo + + sys.modules["state"] = mepo.state + sys.modules["state.component"] = mepo.component + sys.modules["utilities"] = mepo.utilities + + @staticmethod + def mepo1_patch_undo(): + """ + Undo changes made my __mepo1_patch(). Called during + """ + import mepo + + entries_to_remove = ["state", "state.component", "utilities"] + for key in entries_to_remove: + sys.modules.pop(key, None) + + @classmethod + def read_state(cls): + if cls.state_exists(): + with open(cls.get_file(), "r") as fin: + allcomps_s = json.load(fin) + # List of dicts -> state (list of MepoComponent objects) + allcomps = [] + for comp_s in allcomps_s: + comp = MepoComponent().deserialize(comp_s) + # Relative path to absolute + comp.local = os.path.join(cls.get_root_dir(), comp.local) + allcomps.append(comp) + return allcomps + elif cls.state_exists(old_style=True): + print( + colors.YELLOW + + "Detected mepo1 style state\n" + + "Run to permanently convert to mepo2 style" + + colors.RESET + ) + cls.__mepo1_patch() + with open(cls.get_file(old_style=True), "rb") as fin: + allcomps = pickle.load(fin) + else: + raise StateDoesNotExistError("Error! mepo state does not exist") + return allcomps + + @classmethod + def __get_new_state_file(cls): + """Return full path to the new state file to write to""" + if cls.state_exists(): + state_dir = cls.get_dir() + pattern = os.path.join(cls.get_dir(), "state.*.json") + states = [os.path.basename(x) for x in glob.glob(os.path.join(pattern))] + new_state_id = max([int(x.split(".")[1]) for x in states]) + 1 + state_filename = "state." + str(new_state_id) + ".json" + else: + state_dir = os.path.join(os.getcwd(), cls.__state_dir_name) + os.makedirs(state_dir, exist_ok=True) + state_filename = "state.0.json" + return os.path.join(state_dir, state_filename) + + @classmethod + def write_state(cls, allcomps): + new_state_file = cls.__get_new_state_file() + allcomps_s = [] + for comp in allcomps: + # Save relative path (to fixture dir) to state + comp.local = os.path.relpath(comp.local, start=cls.get_root_dir()) + allcomps_s.append(comp.serialize()) + with open(new_state_file, "w") as fout: + json.dump(allcomps_s, fout) + # Make the state file read-only + os.chmod(new_state_file, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) + # Update symlink + state_dir = os.path.dirname(new_state_file) + state_fileptr_fullpath = os.path.join(state_dir, cls.__state_fileptr_name) + if os.path.isfile(state_fileptr_fullpath): + os.remove(state_fileptr_fullpath) + with mepo_chdir(state_dir): + new_state_filename = os.path.basename(new_state_file) + os.symlink(new_state_filename, cls.__state_fileptr_name) diff --git a/src/mepo/state/component.py b/src/mepo/state/component.py deleted file mode 100644 index 5d79f39..0000000 --- a/src/mepo/state/component.py +++ /dev/null @@ -1,216 +0,0 @@ -import os -import shlex -import textwrap - -from collections import namedtuple - -from mepo.utilities.version import MepoVersion -from mepo.utilities import shellcmd, mepoconfig -from urllib.parse import urlparse - -# This will be used to store the "final nodes" from each subrepo -original_final_node_list = [] - -class MepoComponent(object): - - __slots__ = ['name', 'local', 'remote', 'version', 'sparse', 'develop', 'recurse_submodules', 'fixture', 'ignore_submodules'] - - def __init__(self): - self.name = None - self.local = None - self.remote = None - self.version = None - self.sparse = None - self.develop = None - self.recurse_submodules = None - self.fixture = None - self.ignore_submodules = None - - def __repr__(self): - # Older mepo clones will not have ignore_submodules in comp, so - # we need to handle this gracefully - try: - _ignore_submodules = self.ignore_submodules - except AttributeError: - _ignore_submodules = None - - return '{} - local: {}, remote: {}, version: {}, sparse: {}, develop: {}, recurse_submodules: {}, fixture: {}, ignore_submodules: {}'.format( - self.name, self.local, self.remote, self.version, self.sparse, self.develop, self.recurse_submodules, self.fixture, _ignore_submodules) - - def __set_original_version(self, comp_details): - if self.fixture: - cmd_if_branch = 'git symbolic-ref HEAD' - # Have to use 'if not' since 0 is a good status - if not shellcmd.run(cmd_if_branch.split(),status=True): - output = shellcmd.run(cmd_if_branch.split(),output=True).rstrip() - ver_name = output.replace('refs/heads/','') - ver_type = 'b' - is_detached = False - else: - # On some CI systems, git is handled oddly. As such, sometimes - # tags aren't found due to shallow clones - cmd_for_tag = 'git describe --tags' - # Have to use 'if not' since 0 is a good status - if not shellcmd.run(cmd_for_tag.split(),status=True): - ver_name = shellcmd.run(cmd_for_tag.split(),output=True).rstrip() - ver_type = 't' - is_detached = True - else: - # Per internet, describe always should always work, though mepo - # will return weirdness (a grafted branch, probably a hash) - cmd_for_always = 'git describe --always' - ver_name = shellcmd.run(cmd_for_always.split(),output=True).rstrip() - ver_type = 'h' - is_detached = True - else: - if comp_details.get('branch', None): - # SPECIAL HANDLING of 'detached head' branches - ver_name = 'origin/' + comp_details['branch'] - ver_type = 'b' - # we always detach branches from components.yaml - is_detached = True - elif comp_details.get('hash', None): - # Hashes don't have to exist - ver_name = comp_details['hash'] - ver_type = 'h' - is_detached = True - else: - ver_name = comp_details['tag'] # 'tag' key has to exist - ver_type = 't' - is_detached = True - self.version = MepoVersion(ver_name, ver_type, is_detached) - - def __validate_fixture(self, comp_details): - unallowed_keys = ['remote', 'local', 'branch', 'hash', 'tag', 'sparse', 'recurse_submodules', 'ignore_submodules'] - if any([comp_details.get(key) for key in unallowed_keys]): - raise Exception("Fixtures are only allowed fixture and develop") - - def __validate_component(self, comp_name, comp_details): - types_of_git_tags = ['branch', 'tag', 'hash'] - git_tag_intersection = set(types_of_git_tags).intersection(set(comp_details.keys())) - if len(git_tag_intersection) == 0: - raise Exception(textwrap.fill(textwrap.dedent(f''' - Component {comp_name} has none of {types_of_git_tags}. mepo - requires one of them.'''))) - elif len(git_tag_intersection) != 1: - raise Exception(textwrap.fill(textwrap.dedent(f''' - Component {comp_name} has {git_tag_intersection} and only one of - {types_of_git_tags} are allowed.'''))) - - def to_component(self, comp_name, comp_details, comp_style): - self.name = comp_name - self.fixture = comp_details.get('fixture', False) - if self.fixture: - self.__validate_fixture(comp_details) - - self.local = '.' - repo_url = get_current_remote_url() - p = urlparse(repo_url) - last_url_node = p.path.rsplit('/')[-1] - self.remote = "../"+last_url_node - else: - self.__validate_component(comp_name, comp_details) - #print(f"original self.local: {comp_details['local']}") - - # Assume the flag for repostories is commercial-at - repo_flag = '@' - - # To make it easier to loop over the local path, split into a list - local_list = splitall(comp_details['local']) - - # The last node of the path is what we will decorate - last_node = local_list[-1] - - # Add that final node to a list - original_final_node_list.append(last_node) - - # Now we need to decorate all the final nodes since we can have - # nested repos with mepo - for item in original_final_node_list: - try: - # Find the index of every "final node" in a local path - # for nesting - index = local_list.index(item) - - # Decorate all final nodes - local_list[index] = decorate_node(item, repo_flag, comp_style) - except ValueError: - pass - - # Now pull the list of nodes back into a path - self.local = os.path.join(*local_list) - #print(f'final self.local: {self.local}') - - self.remote = comp_details['remote'] - self.sparse = comp_details.get('sparse', None) # sparse is optional - self.develop = comp_details.get('develop', None) # develop is optional - self.recurse_submodules = comp_details.get('recurse_submodules', None) # recurse_submodules is optional - self.ignore_submodules = comp_details.get('ignore_submodules', None) # ignore_submodules is optional - self.__set_original_version(comp_details) - return self - - def to_dict(self, start): - details = dict() - # Fixtures are allowed exactly two entries - if self.fixture: - details['fixture'] = self.fixture - if self.develop: - details['develop'] = self.develop - else: - details['local'] = self.local - details['remote'] = self.remote - if self.version.type == 't': - details['tag'] = self.version.name - elif self.version.type == 'h': - details['hash'] = self.version.name - else: # if not tag or hash, version has to be a branch - if self.version.detached: # SPECIAL HANDLING of 'detached head' branches - details['branch'] = self.version.name.replace('origin/', '') - else: - details['branch'] = self.version.name - if self.sparse: - details['sparse'] = self.sparse - if self.develop: - details['develop'] = self.develop - if self.recurse_submodules: - details['recurse_submodules'] = self.recurse_submodules - if self.ignore_submodules: - details['ignore_submodules'] = self.ignore_submodules - return {self.name: details} - -def get_current_remote_url(): - cmd = 'git remote get-url origin' - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output - -def decorate_node(item, flag, style): - # If we do not pass in a style... - if not style: - # Just use what's in components.yaml - return item - # else use the style - else: - item = item.replace(flag,'') - if style == 'naked': - output = item - elif style == 'prefix': - output = flag + item - elif style == 'postfix': - output = item + flag - return output - -# From https://learning.oreilly.com/library/view/python-cookbook/0596001673/ch04s16.html -def splitall(path): - allparts = [] - while 1: - parts = os.path.split(path) - if parts[0] == path: # sentinel for absolute paths - allparts.insert(0, parts[0]) - break - elif parts[1] == path: # sentinel for relative paths - allparts.insert(0, parts[1]) - break - else: - path = parts[0] - allparts.insert(0, parts[1]) - return allparts diff --git a/src/mepo/state/state.py b/src/mepo/state/state.py deleted file mode 100644 index 6ec4d71..0000000 --- a/src/mepo/state/state.py +++ /dev/null @@ -1,126 +0,0 @@ -import os -import sys -import yaml -import glob -import pickle - -from pathlib import Path - -from mepo.config.config_file import ConfigFile -from mepo.state.component import MepoComponent -from mepo.utilities import shellcmd -from mepo.utilities import colors -from mepo.state.exceptions import StateDoesNotExistError, StateAlreadyInitializedError - -class MepoState(object): - - __state_dir_name = '.mepo' - __state_fileptr_name = 'state.pkl' - - @staticmethod - def get_parent_dirs(): - mypath = os.getcwd() - parentdirs = [mypath] - while mypath != '/': - mypath = os.path.dirname(mypath) - parentdirs.append(mypath) - return parentdirs - - @classmethod - def get_dir(cls): - for mydir in cls.get_parent_dirs(): - state_dir = os.path.join(mydir, cls.__state_dir_name) - if os.path.exists(state_dir): - return state_dir - raise OSError('mepo state dir [.mepo] does not exist') - - @classmethod - def get_root_dir(cls): - '''Return directory that contains .mepo''' - return os.path.dirname(cls.get_dir()) - - @classmethod - def get_file(cls): - state_file = os.path.join(cls.get_dir(), cls.__state_fileptr_name) - if os.path.exists(state_file): - return state_file - raise OSError('mepo state file [%s] does not exist' % state_file) - - @classmethod - def exists(cls): - try: - cls.get_file() - return True - except OSError: - return False - - @classmethod - def initialize(cls, project_config_file, directory_style): - if cls.exists(): - raise StateAlreadyInitializedError('Error! mepo state already exists') - input_components = ConfigFile(project_config_file).read_file() - - num_fixture = 0 - complist = list() - for name, comp in input_components.items(): - # We only allow one fixture - if 'fixture' in comp: - num_fixture += comp['fixture'] - if num_fixture > 1: - raise Exception("Only one fixture allowed") - - complist.append(MepoComponent().to_component(name, comp, directory_style)) - cls.write_state(complist) - - @staticmethod - def __mepo1_patch(): - """ - mepo1 to mepo2 includes reanming of directories - mepo.d/state/state.py -> src/mepo/state/state.py - Since pickle requires that "the class definition must be importable - and live in the same module as when the object was stored", we need to - patch sys.modules to be able to read mepo1 state - """ - print(colors.YELLOW + 'Converting mepo1 state to mepo2 state' + colors.RESET) - import mepo.state - import mepo.utilities - sys.modules['state'] = mepo.state - sys.modules['utilities'] = mepo.utilities - - @classmethod - def read_state(cls): - if not cls.exists(): - raise StateDoesNotExistError('Error! mepo state does not exist') - try: - with open(cls.get_file(), 'rb') as fin: - allcomps = pickle.load(fin) - except ModuleNotFoundError: - cls.__mepo1_patch() - with open(cls.get_file(), 'rb') as fin: - allcomps = pickle.load(fin) - return allcomps - - @classmethod - def write_state(cls, state_details): - if cls.exists(): - state_dir = cls.get_dir() - pattern = os.path.join(cls.get_dir(), 'state.*.pkl') - states = [os.path.basename(x) for x in glob.glob(os.path.join(pattern))] - new_state_id = max([int(x.split('.')[1]) for x in states]) + 1 - state_file_name = 'state.' + str(new_state_id) + '.pkl' - else: - state_dir = os.path.join(os.getcwd(), cls.__state_dir_name) - os.mkdir(state_dir) - state_file_name = 'state.0.pkl' - new_state_file = os.path.join(state_dir, state_file_name) - with open(new_state_file, 'wb') as fout: - pickle.dump(state_details, fout, -1) - state_fileptr = cls.__state_fileptr_name - state_fileptr_fullpath = os.path.join(state_dir, state_fileptr) - if os.path.isfile(state_fileptr_fullpath): - os.remove(state_fileptr_fullpath) - #os.symlink(new_state_file, state_fileptr_fullpath) - curr_dir=os.getcwd() - os.chdir(state_dir) - os.symlink(state_file_name, state_fileptr) - os.chdir(curr_dir) diff --git a/src/mepo/utilities/__init__.py b/src/mepo/utilities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mepo/utilities/chdir.py b/src/mepo/utilities/chdir.py new file mode 100644 index 0000000..1b8922d --- /dev/null +++ b/src/mepo/utilities/chdir.py @@ -0,0 +1,14 @@ +"""contextlib.chdir was introduced in Python 3.11""" + +import os +from contextlib import contextmanager + + +@contextmanager +def chdir(path): + cwd = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(cwd) diff --git a/src/mepo/utilities/colors.py b/src/mepo/utilities/colors.py index 78ca5e6..e258168 100644 --- a/src/mepo/utilities/colors.py +++ b/src/mepo/utilities/colors.py @@ -2,16 +2,16 @@ import colorama from colorama import Fore, Back, Style - RED = Fore.RED - BLUE = Fore.BLUE - CYAN = Fore.CYAN - GREEN = Fore.GREEN + RED = Fore.RED + BLUE = Fore.BLUE + CYAN = Fore.CYAN + GREEN = Fore.GREEN YELLOW = Fore.YELLOW - RESET = Style.RESET_ALL + RESET = Style.RESET_ALL except ImportError: - RED = "\x1b[1;31m" - BLUE = "\x1b[1;34m" - CYAN = "\x1b[1;36m" - GREEN = "\x1b[1;32m" + RED = "\x1b[1;31m" + BLUE = "\x1b[1;34m" + CYAN = "\x1b[1;36m" + GREEN = "\x1b[1;32m" YELLOW = "\x1b[1;33m" - RESET = "\x1b[0;0m" + RESET = "\x1b[0;0m" diff --git a/src/mepo/state/exceptions.py b/src/mepo/utilities/exceptions.py similarity index 75% rename from src/mepo/state/exceptions.py rename to src/mepo/utilities/exceptions.py index a6e0aa0..084a1b6 100644 --- a/src/mepo/state/exceptions.py +++ b/src/mepo/utilities/exceptions.py @@ -1,23 +1,34 @@ class StateDoesNotExistError(SystemExit): """Raised when the mepo state does not exist""" + pass + class StateAlreadyInitializedError(SystemExit): """Raised when the mepo state has already been initialized""" + pass + class RepoAlreadyClonedError(SystemExit): """Raised when the repository has already been cloned""" + pass -class ConfigFileNotFoundError(FileNotFoundError): - """Raised when the config file is not found""" + +class RegistryNotFoundError(FileNotFoundError): + """Raised when the registry is not found""" + pass + class SuffixNotRecognizedError(RuntimeError): - """Raised when the config suffix is not recognized""" + """Raised when the registry suffix is not recognized""" + pass + class NotInRootDirError(SystemExit): """Raised when a command is run not in the root directory""" + pass diff --git a/src/mepo/utilities/mepoconfig.py b/src/mepo/utilities/mepoconfig.py index 0fc8c5d..6451e2d 100644 --- a/src/mepo/utilities/mepoconfig.py +++ b/src/mepo/utilities/mepoconfig.py @@ -2,62 +2,76 @@ import os import sys -config_file = os.path.expanduser('~/.mepoconfig') +config_file = os.path.expanduser("~/.mepoconfig") config = configparser.ConfigParser() config.read(config_file) + def split_entry(entry): - entry_list = entry.split('.') + entry_list = entry.split(".") if len(entry_list) != 2: - raise Exception(f'Invalid entry [{entry}]. Must be of form section.option, e.g., "alias.st"') + raise Exception( + f'Invalid entry [{entry}]. Must be of form section.option, e.g., "alias.st"' + ) section = entry_list[0] option = entry_list[1] return section, option + def write(): - with open(config_file,'w') as fp: + with open(config_file, "w") as fp: config.write(fp) + def print_sections(): print(config.sections()) + def print_options(section): print(config.options(section)) + def print(): config.write(sys.stdout) + def has_section(section): return config.has_section(section) + def has_option(section, option): return config.has_option(section, option) + def get(section, option): return config[section][option] + def remove_option(section, option): config.remove_option(section, option) if not config[section]: config.remove_section(section) + def set(section, option, value): if not has_section(section): config[section] = {} config[section][option] = value + def get_command_alias(command): output = [] - if has_section('alias'): - for key,value in config.items('alias'): + if has_section("alias"): + for key, value in config.items("alias"): if value == command: output.append(key) return output + def get_alias_command(alias): command = alias - if has_section('alias'): - for key,value in config.items('alias'): + if has_section("alias"): + for key, value in config.items("alias"): if key == alias: command = value return command diff --git a/src/mepo/utilities/shellcmd.py b/src/mepo/utilities/shellcmd.py index f735932..f69da20 100644 --- a/src/mepo/utilities/shellcmd.py +++ b/src/mepo/utilities/shellcmd.py @@ -1,11 +1,12 @@ import subprocess as sp + def run(cmd, output=None, stdout=None, status=None): result = sp.run( cmd, - stdout = sp.PIPE, - stderr = sp.PIPE, - universal_newlines = True # result byte sequence -> string + stdout=sp.PIPE, + stderr=sp.PIPE, + universal_newlines=True, # result byte sequence -> string ) if status: diff --git a/src/mepo/utilities/verify.py b/src/mepo/utilities/verify.py index d5fadf0..8e7dfa3 100644 --- a/src/mepo/utilities/verify.py +++ b/src/mepo/utilities/verify.py @@ -11,7 +11,9 @@ def valid_components(specified_comp_names, allcomps, ignore_case=False): """ # Make a list of all the component names depending on ignore_case - all_component_names = [x.name.casefold() if ignore_case else x.name for x in allcomps] + all_component_names = [ + x.name.casefold() if ignore_case else x.name for x in allcomps + ] # Loop over all the components we want to verify... for component_name in specified_comp_names: @@ -22,6 +24,7 @@ def valid_components(specified_comp_names, allcomps, ignore_case=False): # Validate the component _validate_component(component_to_find, all_component_names) + def _validate_component(component, all_components): """ Function to raise exception on invalid component name @@ -32,4 +35,4 @@ def _validate_component(component, all_components): """ if component not in all_components: - raise Exception('Unknown component name [{}]'.format(component)) + raise Exception("Unknown component name [{}]".format(component)) diff --git a/src/mepo/utilities/version.py b/src/mepo/utilities/version.py index 62d74ff..aa4c70f 100644 --- a/src/mepo/utilities/version.py +++ b/src/mepo/utilities/version.py @@ -1,27 +1,29 @@ from collections import namedtuple -MepoVersion = namedtuple('MepoVersion', ['name', 'type', 'detached']) +MepoVersion = namedtuple("MepoVersion", ["name", "type", "detached"]) -def version_to_string(version,git=None): - version_name = version[0] - version_type = version[1] + +def version_to_string(version, git=None): + version_name = version[0] + version_type = version[1] version_detached = version[2] - if version_detached: # detached head + if version_detached: # detached head # We remove the "origin/" from the internal detached branch name # for clarity in mepo status output - version_name = version_name.replace('origin/','') - if version_type == 'b' and git: + version_name = version_name.replace("origin/", "") + if version_type == "b" and git: cur_hash = git.rev_parse(short=True).strip() - s = f'({version_type}) {version_name} (DH, {cur_hash})' + s = f"({version_type}) {version_name} (DH, {cur_hash})" else: - s = f'({version_type}) {version_name} (DH)' + s = f"({version_type}) {version_name} (DH)" else: - s = f'({version_type}) {version_name}' + s = f"({version_type}) {version_name}" return s -def sanitize_version_string(orig,curr,git): - ''' + +def sanitize_version_string(orig, curr, git): + """ This routine tries to figure out if two tags are the same. The issue is that git sometimes returns the "wrong" tag in @@ -29,19 +31,19 @@ def sanitize_version_string(orig,curr,git): if that commit is also tagged with foo, then sometimes mepo will say that things have changed because it thinks it's on foo. - ''' + """ # The trick below only works on tags and hashes (I think), so # if not a tag or hash, just do nothing for now - is_tag = '(t)' - is_hash = '(h)' + is_tag = "(t)" + is_hash = "(h)" # For status, we pass in space-delimited strings that are: # 'type version dh' # So let's split into lists and pull the type # But for save, we are passing in one single string orig_list = orig.split() - if (len(orig_list) > 1): + if len(orig_list) > 1: # Pull out the type orig_type = orig_list[0] # Pull out the version string... @@ -53,7 +55,7 @@ def sanitize_version_string(orig,curr,git): orig_ver = orig_list[0] curr_list = curr.split() - if (len(curr_list) > 1): + if len(curr_list) > 1: # Pull out the type curr_type = curr_list[0] # Pull out the version string... @@ -64,8 +66,8 @@ def sanitize_version_string(orig,curr,git): # version is the only element curr_ver = curr_list[0] - orig_type_is_tag_or_hash = (orig_type == is_tag or orig_type == is_hash) - curr_type_is_tag_or_hash = (curr_type == is_tag or curr_type == is_hash) + orig_type_is_tag_or_hash = orig_type == is_tag or orig_type == is_hash + curr_type_is_tag_or_hash = curr_type == is_tag or curr_type == is_hash # Now if a type or hash... if orig_type_is_tag_or_hash and curr_type_is_tag_or_hash: @@ -84,7 +86,7 @@ def sanitize_version_string(orig,curr,git): curr_list[curr_list.index(curr_type)] = orig_type # And then remake the curr string - curr = ' '.join(curr_list) + curr = " ".join(curr_list) # And return curr return curr diff --git a/tests/input/args.py b/tests/input/args.py deleted file mode 100644 index 7e2cb0c..0000000 --- a/tests/input/args.py +++ /dev/null @@ -1,2 +0,0 @@ -config_file = None -allrepos = None diff --git a/tests/output/compare_brief_output.txt b/tests/output/compare_brief_output.txt deleted file mode 100644 index f7f32d0..0000000 --- a/tests/output/compare_brief_output.txt +++ /dev/null @@ -1,5 +0,0 @@ -Repo | Original | Current --------- | -------------------- | ------- -env | (t) v4.8.0 (DH) | (b) main -cmake | (t) v3.21.0 (DH) | (b) develop -fvdycore | (t) geos/v1.5.0 (DH) | (b) geos/develop diff --git a/tests/output/compare_full_output.txt b/tests/output/compare_full_output.txt deleted file mode 100644 index 02aaa0b..0000000 --- a/tests/output/compare_full_output.txt +++ /dev/null @@ -1,11 +0,0 @@ -Repo | Original | Current ----------------------- | -------------------------------- | ------- -GEOSfvdycore | (t) v1.13.0 (DH) | (t) v1.13.0 (DH) -env | (t) v4.8.0 (DH) | (b) main -cmake | (t) v3.21.0 (DH) | (b) develop -ecbuild | (t) geos/v1.3.0 (DH) | (t) geos/v1.3.0 (DH) -GMAO_Shared | (t) v1.6.3 (DH) | (t) v1.6.3 (DH) -MAPL | (t) v2.33.0 (DH) | (t) v2.33.0 (DH) -FMS | (t) geos/2019.01.02+noaff.8 (DH) | (t) geos/2019.01.02+noaff.8 (DH) -FVdycoreCubed_GridComp | (t) v1.12.1 (DH) | (t) v1.12.1 (DH) -fvdycore | (t) geos/v1.5.0 (DH) | (b) geos/develop diff --git a/tests/output/list_output.txt b/tests/output/list_output.txt deleted file mode 100644 index 608fc12..0000000 --- a/tests/output/list_output.txt +++ /dev/null @@ -1 +0,0 @@ -GEOSfvdycore env cmake ecbuild GMAO_Shared MAPL FMS FVdycoreCubed_GridComp fvdycore diff --git a/tests/output/output_branch_create.txt b/tests/output/output_branch_create.txt new file mode 100644 index 0000000..7d782bb --- /dev/null +++ b/tests/output/output_branch_create.txt @@ -0,0 +1 @@ ++ ecbuild: the-best-branch-ever diff --git a/tests/output/output_branch_delete.txt b/tests/output/output_branch_delete.txt new file mode 100644 index 0000000..8292ada --- /dev/null +++ b/tests/output/output_branch_delete.txt @@ -0,0 +1 @@ +- ecbuild: the-best-branch-ever diff --git a/tests/output/output_branch_list.txt b/tests/output/output_branch_list.txt new file mode 100644 index 0000000..13438e5 --- /dev/null +++ b/tests/output/output_branch_list.txt @@ -0,0 +1,11 @@ +ecbuild | * (HEAD detached at geos/v1.3.0) + | release/stable + | remotes/origin/HEAD -> origin/release/stable + | remotes/origin/develop + | remotes/origin/feature/FindMKL-portability-improvement + | remotes/origin/feature/ecbuild_use_package_quiet + | remotes/origin/feature/mathomp4/add-jemalloc + | remotes/origin/feature/netcdf4-cmake + | remotes/origin/geos/main + | remotes/origin/master + | remotes/origin/release/stable diff --git a/tests/output/output_clone_status.txt b/tests/output/output_clone_status.txt new file mode 100644 index 0000000..c59943d --- /dev/null +++ b/tests/output/output_clone_status.txt @@ -0,0 +1,11 @@ +Checking status... +GEOSfvdycore | (t) v2.13.0 (DH) +env | (t) v4.25.1 (DH) +cmake | (t) v3.38.0 (DH) +ecbuild | (t) geos/v1.3.0 (DH) +GMAO_Shared | (t) v1.9.6 (DH) +GEOS_Util | (t) v2.0.5 (DH) +MAPL | (t) v2.43.0 (DH) +FMS | (t) geos/2019.01.02+noaff.8 (DH) +FVdycoreCubed_GridComp | (t) v2.10.0 (DH) +fvdycore | (t) geos/v2.8.1 (DH) diff --git a/tests/output/output_compare.txt b/tests/output/output_compare.txt new file mode 100644 index 0000000..59da485 --- /dev/null +++ b/tests/output/output_compare.txt @@ -0,0 +1,3 @@ +Repo | Original | Current +---- | ---------------- | ------- +MAPL | (t) v2.43.0 (DH) | (b) develop diff --git a/tests/output/output_compare_all.txt b/tests/output/output_compare_all.txt new file mode 100644 index 0000000..ddf0ea7 --- /dev/null +++ b/tests/output/output_compare_all.txt @@ -0,0 +1,12 @@ +Repo | Original | Current +---------------------- | -------------------------------- | ------- +GEOSfvdycore | (t) v2.13.0 (DH) | (t) v2.13.0 (DH) +env | (t) v4.25.1 (DH) | (t) v4.25.1 (DH) +cmake | (t) v3.38.0 (DH) | (t) v3.38.0 (DH) +ecbuild | (t) geos/v1.3.0 (DH) | (t) geos/v1.3.0 (DH) +GMAO_Shared | (t) v1.9.6 (DH) | (t) v1.9.6 (DH) +GEOS_Util | (t) v2.0.5 (DH) | (t) v2.0.5 (DH) +MAPL | (t) v2.43.0 (DH) | (b) develop +FMS | (t) geos/2019.01.02+noaff.8 (DH) | (t) geos/2019.01.02+noaff.8 (DH) +FVdycoreCubed_GridComp | (t) v2.10.0 (DH) | (t) v2.10.0 (DH) +fvdycore | (t) geos/v2.8.1 (DH) | (t) geos/v2.8.1 (DH) diff --git a/tests/output/status_output.txt b/tests/output/output_develop_status.txt similarity index 53% rename from tests/output/status_output.txt rename to tests/output/output_develop_status.txt index 1b4a156..777faf4 100644 --- a/tests/output/status_output.txt +++ b/tests/output/output_develop_status.txt @@ -1,10 +1,11 @@ Checking status... -GEOSfvdycore | (t) v1.13.0 (DH) +GEOSfvdycore | (t) v2.13.0 (DH) env | (b) main cmake | (b) develop ecbuild | (t) geos/v1.3.0 (DH) -GMAO_Shared | (t) v1.6.3 (DH) -MAPL | (t) v2.33.0 (DH) +GMAO_Shared | (t) v1.9.6 (DH) +GEOS_Util | (t) v2.0.5 (DH) +MAPL | (t) v2.43.0 (DH) FMS | (t) geos/2019.01.02+noaff.8 (DH) -FVdycoreCubed_GridComp | (t) v1.12.1 (DH) +FVdycoreCubed_GridComp | (t) v2.10.0 (DH) fvdycore | (b) geos/develop diff --git a/tests/output/output_diff.txt b/tests/output/output_diff.txt new file mode 100644 index 0000000..b952162 --- /dev/null +++ b/tests/output/output_diff.txt @@ -0,0 +1,5 @@ +Diffing... +FVdycoreCubed_GridComp (location: .): + +GEOS_FV3_Utilities.F90 +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── diff --git a/tests/output/output_list.txt b/tests/output/output_list.txt new file mode 100644 index 0000000..323e5c3 --- /dev/null +++ b/tests/output/output_list.txt @@ -0,0 +1 @@ +GEOSfvdycore env cmake ecbuild GMAO_Shared GEOS_Util MAPL FMS FVdycoreCubed_GridComp fvdycore diff --git a/tests/output/output_pull_all.txt b/tests/output/output_pull_all.txt new file mode 100644 index 0000000..108a2d1 --- /dev/null +++ b/tests/output/output_pull_all.txt @@ -0,0 +1 @@ +The following repos were not pulled (detached HEAD): GEOSfvdycore, env, cmake, ecbuild, GMAO_Shared, GEOS_Util, MAPL, FMS, FVdycoreCubed_GridComp, fvdycore diff --git a/tests/output/output_push.txt b/tests/output/output_push.txt new file mode 100644 index 0000000..9e7089a --- /dev/null +++ b/tests/output/output_push.txt @@ -0,0 +1,7 @@ +fatal: You are not currently on a branch. +To push the history leading to the current (detached HEAD) +state now, use + + git push https://github.com/GEOS-ESM/FVdycoreCubed_GridComp.git HEAD: + + diff --git a/tests/output/output_reset.txt b/tests/output/output_reset.txt new file mode 100644 index 0000000..a51043d --- /dev/null +++ b/tests/output/output_reset.txt @@ -0,0 +1,10 @@ +Removing src/Components/@FVdycoreCubed_GridComp/@fvdycore...done. +Removing src/Components/@FVdycoreCubed_GridComp...done. +Removing src/Shared/@FMS...done. +Removing src/Shared/@MAPL...done. +Removing src/Shared/@GMAO_Shared/@GEOS_Util...done. +Removing src/Shared/@GMAO_Shared...done. +Removing @cmake/@ecbuild...done. +Removing @cmake...done. +Removing @env...done. +Removing mepo state...done. diff --git a/tests/output/output_tag_create.txt b/tests/output/output_tag_create.txt new file mode 100644 index 0000000..2ed1aeb --- /dev/null +++ b/tests/output/output_tag_create.txt @@ -0,0 +1,2 @@ ++ MAPL: new-awesome-tag ++ FMS: new-awesome-tag diff --git a/tests/output/output_tag_delete.txt b/tests/output/output_tag_delete.txt new file mode 100644 index 0000000..58291cc --- /dev/null +++ b/tests/output/output_tag_delete.txt @@ -0,0 +1,2 @@ +- MAPL: new-awesome-tag +- FMS: new-awesome-tag diff --git a/tests/output/output_whereis.txt b/tests/output/output_whereis.txt new file mode 100644 index 0000000..1daad36 --- /dev/null +++ b/tests/output/output_whereis.txt @@ -0,0 +1,10 @@ +GEOSfvdycore | . +env | @env +cmake | @cmake +ecbuild | @cmake/@ecbuild +GMAO_Shared | src/Shared/@GMAO_Shared +GEOS_Util | src/Shared/@GMAO_Shared/@GEOS_Util +MAPL | src/Shared/@MAPL +FMS | src/Shared/@FMS +FVdycoreCubed_GridComp | src/Components/@FVdycoreCubed_GridComp +fvdycore | src/Components/@FVdycoreCubed_GridComp/@fvdycore diff --git a/tests/test_mepo_commands.py b/tests/test_mepo_commands.py index dbdd88e..bb5b8ad 100644 --- a/tests/test_mepo_commands.py +++ b/tests/test_mepo_commands.py @@ -1,116 +1,346 @@ import os -import sys -THIS_DIR = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(THIS_DIR, '..', 'src')) + +import io import shutil import shlex import unittest +import importlib +import contextlib import subprocess as sp -from io import StringIO +from types import SimpleNamespace + +import mepo.command.clone as mepo_clone +import mepo.command.list as mepo_list +import mepo.command.status as mepo_status +import mepo.command.compare as mepo_compare +import mepo.command.develop as mepo_develop +import mepo.command.checkout as mepo_checkout +import mepo.command.branch_list as mepo_branch_list +import mepo.command.branch_create as mepo_branch_create +import mepo.command.branch_delete as mepo_branch_delete +import mepo.command.tag_list as mepo_tag_list +import mepo.command.tag_create as mepo_tag_create +import mepo.command.tag_delete as mepo_tag_delete +import mepo.command.fetch as mepo_fetch +import mepo.command.pull as mepo_pull +import mepo.command.push as mepo_push +import mepo.command.diff as mepo_diff +import mepo.command.whereis as mepo_whereis +import mepo.command.reset as mepo_reset + +# Import commands with dash in the name +mepo_restore_state = importlib.import_module("mepo.command.restore-state") +mepo_checkout_if_exists = importlib.import_module("mepo.command.checkout-if-exists") +mepo_pull_all = importlib.import_module("mepo.command.pull-all") -from input import args +TEST_DIR = os.path.dirname(os.path.realpath(__file__)) -from mepo.command.init import init as mepo_init -from mepo.command.clone import clone as mepo_clone -from mepo.command.list import list as mepo_list -from mepo.command.status import status as mepo_status -from mepo.command.compare import compare as mepo_compare -from mepo.command.develop import develop as mepo_develop class TestMepoCommands(unittest.TestCase): - maxDiff=None + @classmethod + def __get_saved_output(cls, output_file): + with open(os.path.join(cls.output_dir, output_file), "r") as fin: + saved_output = fin.read() + return saved_output @classmethod def __checkout_fixture(cls): - remote = 'https://github.com/GEOS-ESM/{}.git'.format(cls.fixture) - cmd = 'git clone -b {} {} {}'.format(cls.tag, remote, cls.fixture_dir) + remote = f"https://github.com/GEOS-ESM/{cls.fixture}.git" + git_clone = "git clone " + if cls.tag: + git_clone += f"-b {cls.tag}" + cmd = f"{git_clone} {remote} {cls.fixture_dir}" sp.run(shlex.split(cmd)) @classmethod def __copy_config_file(cls): - src = os.path.join(cls.input_dir, 'components.yaml') + src = os.path.join(cls.input_dir, "components.yaml") dst = os.path.join(cls.fixture_dir) shutil.copy(src, dst) + @classmethod + def __mepo_clone(cls): + # mepo clone + args = SimpleNamespace( + style="prefix", + registry=None, + repo_url=None, + allrepos=None, + branch=None, + directory=None, + partial="blobless", + ) + mepo_clone.run(args) + print(flush=True) + @classmethod def setUpClass(cls): - cls.input_dir = os.path.join(THIS_DIR, 'input') - cls.output_dir = os.path.join(THIS_DIR, 'output') - cls.fixture = 'GEOSfvdycore' - cls.tag = 'v1.13.0' - cls.tmpdir = os.path.join(THIS_DIR, 'tmp') + cls.input_dir = os.path.join(TEST_DIR, "input") + cls.output_dir = os.path.join(TEST_DIR, "output") + cls.output_clone_status = cls.__get_saved_output("output_clone_status.txt") + cls.fixture = "GEOSfvdycore" + cls.tag = "v2.13.0" + cls.tmpdir = os.path.join(TEST_DIR, "tmp") cls.fixture_dir = os.path.join(cls.tmpdir, cls.fixture) if os.path.isdir(cls.fixture_dir): shutil.rmtree(cls.fixture_dir) cls.__checkout_fixture() - #cls.__copy_config_file() - args.config = 'components.yaml' - args.style = 'prefix' os.chdir(cls.fixture_dir) - mepo_init.run(args) - args.config = None - args.repo_url = None - args.branch = None - args.directory = None - args.partial = 'blobless' - mepo_clone.run(args) - # In order to better test compare, we need to do *something* - args.comp_name = ['env','cmake','fvdycore'] - args.quiet = False - mepo_develop.run(args) + cls.__mepo_clone() def setUp(self): pass + def __mepo_status(self, saved_output): + """saved_output is either a string or a filename""" + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + ignore_permissions=False, + nocolor=True, + hashes=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_status.run(args) + try: # assume saved_output is a file + saved_output_s = self.__class__.__get_saved_output(saved_output) + except FileNotFoundError: + saved_output_s = saved_output + self.assertEqual(output.getvalue(), saved_output_s) + + def __mepo_restore_state(self): + os.chdir(self.__class__.fixture_dir) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_restore_state.run(SimpleNamespace()) + self.__mepo_status(self.__class__.output_clone_status) + def test_list(self): - sys.stdout = output = StringIO() - mepo_list.run(args) - sys.stdout = sys.__stdout__ - with open(os.path.join(self.__class__.output_dir, 'list_output.txt'), 'r') as fin: - saved_output = fin.read() + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace(one_per_line=False) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_list.run(args) + saved_output = self.__class__.__get_saved_output("output_list.txt") self.assertEqual(output.getvalue(), saved_output) - def test_status(self): - sys.stdout = output = StringIO() - args.ignore_permissions=False - args.nocolor=True - args.hashes=False - mepo_status.run(args) - sys.stdout = sys.__stdout__ - with open(os.path.join(self.__class__.output_dir, 'status_output.txt'), 'r') as fin: - saved_output = fin.read() + def test_develop(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["env", "cmake", "fvdycore"], + quiet=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_develop.run(args) + self.__mepo_status("output_develop_status.txt") + # Clean up + self.__mepo_restore_state() + + def test_checkout_compare(self): + os.chdir(self.__class__.fixture_dir) + # Checkout "develop" branch of MAPL and env + args = SimpleNamespace( + branch_name="develop", + comp_name=["MAPL"], + b=False, + quiet=False, + detach=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_checkout.run(args) + # Compare (default) + args_cmp = SimpleNamespace( + all=False, + nocolor=True, + wrap=True, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_compare.run(args_cmp) + saved_output = self.__class__.__get_saved_output("output_compare.txt") self.assertEqual(output.getvalue(), saved_output) + # Compare (All) + args_cmp.all = True + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_compare.run(args_cmp) + saved_output = self.__class__.__get_saved_output("output_compare_all.txt") + self.assertEqual(output.getvalue(), saved_output) + # Clean up + self.__mepo_restore_state() - def test_compare_brief(self): - sys.stdout = output = StringIO() - args.all=False - args.nocolor=True - args.wrap=True - mepo_compare.run(args) - sys.stdout = sys.__stdout__ - with open(os.path.join(self.__class__.output_dir, 'compare_brief_output.txt'), 'r') as fin: - saved_output = fin.read() + def test_checkout_if_exists(self): + # Fixture component + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + ref_name="not-expected-to-exist", + quiet=True, + detach=False, + dry_run=False, + ) + mepo_checkout_if_exists.run(args) + # Since we do not expect this ref to exist, status should be that of clone + self.__mepo_status(self.__class__.output_clone_status) + + def test_branch_list(self): + os.chdir(self.__class__.fixture_dir) + # Not expecting new branches in this component (fingers crossed) + args = SimpleNamespace( + comp_name=["ecbuild"], + all=True, + nocolor=True, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_branch_list.run(args) + saved_output = self.__class__.__get_saved_output("output_branch_list.txt") self.assertEqual(output.getvalue(), saved_output) - def test_compare_full(self): - sys.stdout = output = StringIO() - args.all=True - args.nocolor=True - args.wrap=True - mepo_compare.run(args) - sys.stdout = sys.__stdout__ - with open(os.path.join(self.__class__.output_dir, 'compare_full_output.txt'), 'r') as fin: - saved_output = fin.read() + def test_branch_create_delete(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["ecbuild"], + branch_name="the-best-branch-ever", + ) + # Create branch + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_branch_create.run(args) + saved_output = self.__class__.__get_saved_output("output_branch_create.txt") + self.assertEqual(output.getvalue(), saved_output) + # Delete the branch that was just created + args.force = False + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_branch_delete.run(args) + saved_output = self.__class__.__get_saved_output("output_branch_delete.txt") + self.assertEqual(output.getvalue(), saved_output) + + def test_tag_list(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace(comp_name=["env"]) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_tag_list.run(args) + self.assertTrue("cuda11.7.0-gcc11.2.0nvptx-openmpi4.0.6" in output.getvalue()) + + def test_tag_create_delete(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["FMS", "MAPL"], + tag_name="new-awesome-tag", + annotate=False, + message=None, + ) + # Create tag + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_tag_create.run(args) + saved_output = self.__class__.__get_saved_output("output_tag_create.txt") + self.assertEqual(output.getvalue(), saved_output) + # Delete the tag that was just created + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_tag_delete.run(args) + saved_output = self.__class__.__get_saved_output("output_tag_delete.txt") + self.assertEqual(output.getvalue(), saved_output) + + def test_fetch(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["FVdycoreCubed_GridComp"], + all=True, + prune=True, + tags=True, + force=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_fetch.run(args) + saved_output = "Fetching \x1b[1;33mFVdycoreCubed_GridComp\x1b[0;0m\n" + self.assertEqual(output.getvalue(), saved_output) + + def test_pull(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["FVdycoreCubed_GridComp"], + quiet=False, + ) + err_msg = "FVdycoreCubed_GridComp has detached head! Cannot pull." + with self.assertRaisesRegex(Exception, err_msg): + mepo_pull.run(args) + + def test_pull_all(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["FVdycoreCubed_GridComp"], + quiet=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_pull_all.run(args) + saved_output = self.__class__.__get_saved_output("output_pull_all.txt") + self.assertEqual(output.getvalue(), saved_output) + + def test_push(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=["FVdycoreCubed_GridComp"], + quiet=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + with self.assertRaises(sp.CalledProcessError): + mepo_push.run(args) + saved_output = self.__class__.__get_saved_output("output_push.txt") self.assertEqual(output.getvalue(), saved_output) + def test_diff(self): + os.chdir(self.__class__.fixture_dir) + os.chdir("./src/Components/@FVdycoreCubed_GridComp") + filename = "GEOS_FV3_Utilities.F90" + # Add a line + with open(filename, "w") as fout: + fout.write(" ") + args = SimpleNamespace( + comp_name=["FVdycoreCubed_GridComp"], + name_only=True, + name_status=False, + ignore_permissions=False, + staged=False, + ignore_space_change=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_diff.run(args) + saved_output = self.__class__.__get_saved_output("output_diff.txt") + # Ignore the last line of output (horizontal line + # with length that of the width of the terminal) + self.assertEqual(output.getvalue().split()[:-1], saved_output.split()[:-1]) + # Clean up + sp.run(f"git checkout {filename}".split(), stderr=sp.DEVNULL) + self.__mepo_status(self.__class__.output_clone_status) + + def test_whereis(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + comp_name=None, + ignore_case=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_whereis.run(args) + saved_output = self.__class__.__get_saved_output("output_whereis.txt") + self.assertEqual(output.getvalue(), saved_output) + + def test_reset(self): + os.chdir(self.__class__.fixture_dir) + args = SimpleNamespace( + force=True, + reclone=False, + dry_run=False, + ) + with contextlib.redirect_stdout(io.StringIO()) as output: + mepo_reset.run(args) + saved_output = self.__class__.__get_saved_output("output_reset.txt") + self.assertEqual(output.getvalue(), saved_output) + # Clean up - reclone (suppress output) + with contextlib.redirect_stdout(io.StringIO()) as output: + self.__class__.__mepo_clone() + def tearDown(self): pass @classmethod def tearDownClass(cls): - os.chdir(THIS_DIR) + os.chdir(TEST_DIR) shutil.rmtree(cls.tmpdir) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() From 8c90e80074bd27547a6a2cbf936082a8d4c3b64e Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 17 May 2024 15:33:49 -0500 Subject: [PATCH 19/27] Make doc generation work again (#286) --- docs/make_md_docs.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/make_md_docs.py b/docs/make_md_docs.py index 7fbab10..1df7830 100644 --- a/docs/make_md_docs.py +++ b/docs/make_md_docs.py @@ -26,7 +26,6 @@ def get_command_list(directory): # Get all commands all_commands_py = glob.glob(os.path.join(directory, "*.py")) all_commands = [os.path.basename(x).replace(".py", "") for x in all_commands_py] - all_commands.remove("command") # manually remove # Now let's find the commands that have subcommands ## First we get commands with underscore @@ -45,9 +44,7 @@ def create_markdown_from_usage(command, mdFile): # Some commands have spaces, so we need to break it up again cmd = " ".join(cmd).split() - result = sp.run( - cmd, capture_output=True, universal_newlines=True, env={"COLUMNS": "256"} - ) + result = sp.run(cmd, capture_output=True, universal_newlines=True) output = result.stdout output_list = output.split("\n") From d4eab4da0480b0433d68ecd7077aacaa7f82ce02 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Tue, 21 May 2024 15:59:56 -0500 Subject: [PATCH 20/27] Switch from Poetry to Rye for dependency management and packaging (#290) * Switch from poetry to rye (#287) * mdutils is a production requirement * pyproject.toml - packages for sdist and wheel builds * Getting hatch to build mepo wheel correctly * Updated CHANGELOG.md * Added colorama as a dependency. Updated tests * Made linter a little happier --- .github/workflows/run-formatter.yaml | 2 +- .github/workflows/run-linter.yaml | 2 +- .github/workflows/run-tests.yaml | 2 +- .gitignore | 3 +- CHANGELOG.md | 14 +- README.md | 2 +- docs/make_md_docs.py | 1 - poetry.lock | 313 --------------------------- pyproject.toml | 47 ++-- requirements-dev.lock | 52 +++++ requirements.lock | 16 ++ requirements.txt | 17 -- src/mepo/__init__.py | 7 + src/mepo/cmdline/branch_parser.py | 5 +- src/mepo/cmdline/config_parser.py | 14 +- src/mepo/cmdline/parser.py | 6 +- src/mepo/cmdline/stash_parser.py | 7 +- src/mepo/cmdline/tag_parser.py | 5 +- tests/test_mepo_commands.py | 6 +- 19 files changed, 142 insertions(+), 379 deletions(-) delete mode 100644 poetry.lock create mode 100644 requirements-dev.lock create mode 100644 requirements.lock delete mode 100644 requirements.txt diff --git a/.github/workflows/run-formatter.yaml b/.github/workflows/run-formatter.yaml index 3f0351a..2d42054 100644 --- a/.github/workflows/run-formatter.yaml +++ b/.github/workflows/run-formatter.yaml @@ -16,7 +16,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -r requirements-dev.lock timeout-minutes: 5 - name: Run black run: black --check . diff --git a/.github/workflows/run-linter.yaml b/.github/workflows/run-linter.yaml index b4fb21c..0607441 100644 --- a/.github/workflows/run-linter.yaml +++ b/.github/workflows/run-linter.yaml @@ -16,7 +16,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -r requirements-dev.lock timeout-minutes: 5 - name: Run pylint run: pylint --exit-zero src/mepo diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 87c4cd5..bbddc13 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -r requirements.lock timeout-minutes: 5 - name: Run tests diff --git a/.gitignore b/.gitignore index 3ce728f..3e8c1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *.pyc *.egg-info dist -venv \ No newline at end of file +venv +.python-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d73ca4..f35af84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `pyproject.toml` to aid with `pip` installation. +- Engineering + -- Formatting with Black + -- Linting with Pylint + -- Dependency management and packaging with Rye + +- Added tests to cover more `mepo` commands + +- Add new command `update-state` to permanently convert mepo1 style state to mepo2 + ### Changed - Converted `mepo` to a Python project via the following renaming @@ -22,10 +31,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 -- Renamed `doc` --> `docs` -- A `mepo` config file is now called a `mepo` registry -- More code reorganization - + - Helper script `mepo`, used for development, moved to the `bin` directory. -- Expanded testing to cover more `mepo` commands +- State: pickle format (mepo1 style) to json format (mepo2 style) + -- If mepo1 style state is detected, print warning and suggest running `mepo update-state` ## [1.52.0] - 2024-01-10 diff --git a/README.md b/README.md index bdc8585..d7c0192 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# mepo [![Actions Status](https://github.com/pchakraborty/mepo/workflows/Unit%20testing%20of%20mepo/badge.svg)](https://github.com/pchakraborty/mepo/actions) [![DOI](https://zenodo.org/badge/215067850.svg)](https://zenodo.org/badge/latestdoi/215067850) +# mepo [![Actions Status](https://github.com/pchakraborty/mepo/workflows/Unit%20testing%20of%20mepo/badge.svg)](https://github.com/pchakraborty/mepo/actions) [![DOI](https://zenodo.org/badge/215067850.svg)](https://zenodo.org/badge/latestdoi/215067850) [![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye-up.com) `mepo` is a tool, written in Python3 (3.9.0+), to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. diff --git a/docs/make_md_docs.py b/docs/make_md_docs.py index 1df7830..485cc65 100644 --- a/docs/make_md_docs.py +++ b/docs/make_md_docs.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import os -import io import glob from mdutils.mdutils import MdUtils import subprocess as sp diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index e6a0607..0000000 --- a/poetry.lock +++ /dev/null @@ -1,313 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "astroid" -version = "3.1.0" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "black" -version = "24.4.2" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, - {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, - {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, - {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, - {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, - {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, - {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, - {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, - {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, - {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, - {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, - {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, - {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, - {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, - {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, - {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, - {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, - {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, - {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, - {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, - {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, - {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdutils" -version = "1.6.0" -description = "Useful package for creating Markdown files while executing python code." -optional = false -python-versions = "^3.6" -files = [ - {file = "mdutils-1.6.0.tar.gz", hash = "sha256:647f3cf00df39fee6c57fa6738dc1160fce1788276b5530c87d43a70cdefdaf1"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] - -[[package]] -name = "pylint" -version = "3.1.0" -description = "python code static checker" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, -] - -[package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.12.4" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, -] - -[[package]] -name = "typing-extensions" -version = "4.11.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "6d1408dadf47789115389ea091cb4fc463e03cce68426149f680c3702272342e" diff --git a/pyproject.toml b/pyproject.toml index dd994ea..ea661cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,22 +1,43 @@ -[tool.poetry] +[project] name = "mepo" version = "2.0.0" description = "A tool for managing (m)ultiple r(epo)s" -authors = ["GMAO SI Team "] -license = "Apache-2.0" +authors = [{name="GMAO SI Team", email="siteam@gmao.gsfc.nasa.gov"}] +dependencies = [ + "pyyaml>=6.0.1", + "mdutils>=1.6.0", + "colorama>=0.4.6", +] readme = "README.md" +license = "Apache-2.0" repository = "https://github.com/GEOS-ESM/mepo.git" +requires-python = ">= 3.9" -[tool.poetry.dependencies] -python = "^3.9" -PyYAML = "^6.0.1" -black = "^24.4.2" -mdutils = "^1.6.0" -pylint = "^3.1.0" - -[tool.poetry.scripts] +[project.scripts] mepo = "mepo.__main__:main" +[tool.rye] +managed = true +dev-dependencies = [ + "black>=24.4.2", + "pylint>=3.2.0", + "flake8>=7.0.0", +] + [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.build.targets.sdist] +only_include = [ + "docs", + "etc", + "src/mepo", + "tests", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/mepo"] diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 0000000..45d9811 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,52 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false +# with-sources: false + +-e file:. +astroid==3.2.1 + # via pylint +black==24.4.2 +click==8.1.7 + # via black +colorama==0.4.6 + # via mepo +dill==0.3.8 + # via pylint +flake8==7.0.0 +isort==5.13.2 + # via pylint +mccabe==0.7.0 + # via flake8 + # via pylint +mdutils==1.6.0 + # via mepo +mypy-extensions==1.0.0 + # via black +packaging==24.0 + # via black +pathspec==0.12.1 + # via black +platformdirs==4.2.2 + # via black + # via pylint +pycodestyle==2.11.1 + # via flake8 +pyflakes==3.2.0 + # via flake8 +pylint==3.2.0 +pyyaml==6.0.1 + # via mepo +tomli==2.0.1 + # via black + # via pylint +tomlkit==0.12.5 + # via pylint +typing-extensions==4.11.0 + # via astroid + # via black + # via pylint diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 0000000..70e68bb --- /dev/null +++ b/requirements.lock @@ -0,0 +1,16 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false +# with-sources: false + +-e file:. +colorama==0.4.6 + # via mepo +mdutils==1.6.0 + # via mepo +pyyaml==6.0.1 + # via mepo diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 873be6c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -astroid==3.1.0 ; python_version >= "3.9" and python_version < "4.0" -black==24.4.2 ; python_version >= "3.9" and python_version < "4.0" -click==8.1.7 ; python_version >= "3.9" and python_version < "4.0" -colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") -dill==0.3.8 ; python_version >= "3.9" and python_version < "4.0" -isort==5.13.2 ; python_version >= "3.9" and python_version < "4.0" -mccabe==0.7.0 ; python_version >= "3.9" and python_version < "4.0" -mdutils==1.6.0 ; python_version >= "3.9" and python_version < "4.0" -mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "4.0" -packaging==24.0 ; python_version >= "3.9" and python_version < "4.0" -pathspec==0.12.1 ; python_version >= "3.9" and python_version < "4.0" -platformdirs==4.2.1 ; python_version >= "3.9" and python_version < "4.0" -pylint==3.1.0 ; python_version >= "3.9" and python_version < "4.0" -pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0" -tomli==2.0.1 ; python_version >= "3.9" and python_version < "3.11" -tomlkit==0.12.4 ; python_version >= "3.9" and python_version < "4.0" -typing-extensions==4.11.0 ; python_version >= "3.9" and python_version < "3.11" diff --git a/src/mepo/__init__.py b/src/mepo/__init__.py index e69de29..85aa9dc 100644 --- a/src/mepo/__init__.py +++ b/src/mepo/__init__.py @@ -0,0 +1,7 @@ +"""Ensure Python version""" + +import sys + +PY_VERSION = (3, 9, 0) +if sys.version_info < PY_VERSION: + sys.exit(f"ERROR: Python version needs to be >= {PY_VERSION}") diff --git a/src/mepo/cmdline/branch_parser.py b/src/mepo/cmdline/branch_parser.py index db0cad5..02f9bd9 100644 --- a/src/mepo/cmdline/branch_parser.py +++ b/src/mepo/cmdline/branch_parser.py @@ -1,7 +1,4 @@ -import argparse - - -class MepoBranchArgParser(object): +class MepoBranchArgParser: def __init__(self, branch): self.branch_subparsers = branch.add_subparsers() diff --git a/src/mepo/cmdline/config_parser.py b/src/mepo/cmdline/config_parser.py index 6516076..361c682 100644 --- a/src/mepo/cmdline/config_parser.py +++ b/src/mepo/cmdline/config_parser.py @@ -1,8 +1,4 @@ -import argparse -import textwrap - - -class MepoConfigArgParser(object): +class MepoConfigArgParser: def __init__(self, config): self.config = config.add_subparsers() @@ -26,7 +22,7 @@ def __get(self): get.add_argument("entry", metavar="entry", help="Entry to display.") def __set(self): - set = self.config.add_parser( + set_ = self.config.add_parser( "set", description=( "Set config `entry` to `value` in `.mepoconfig`. " @@ -34,8 +30,8 @@ def __set(self): "So to set an `alias` for `status` of `st` You would run `mepo config set alias.st status`" ), ) - set.add_argument("entry", metavar="entry", help="Entry to set.") - set.add_argument("value", metavar="value", help="Value to set entry to.") + set_.add_argument("entry", metavar="entry", help="Entry to set.") + set_.add_argument("value", metavar="value", help="Value to set entry to.") def __delete(self): delete = self.config.add_parser( @@ -49,6 +45,6 @@ def __delete(self): delete.add_argument("entry", metavar="entry", help="Entry to delete.") def __print(self): - print = self.config.add_parser( + _ = self.config.add_parser( "print", description="Print contents of `.mepoconfig`" ) diff --git a/src/mepo/cmdline/parser.py b/src/mepo/cmdline/parser.py index 295b76e..ee71208 100644 --- a/src/mepo/cmdline/parser.py +++ b/src/mepo/cmdline/parser.py @@ -8,7 +8,7 @@ from ..utilities import mepoconfig -class MepoArgParser(object): +class MepoArgParser: __slots__ = ["parser", "subparsers"] @@ -152,7 +152,7 @@ def __status(self): ) def __restore_state(self): - restore_state = self.subparsers.add_parser( + _ = self.subparsers.add_parser( "restore-state", description="Restores all components to the last saved state.", aliases=mepoconfig.get_command_alias("restore-state"), @@ -490,7 +490,7 @@ def __config(self): MepoConfigArgParser(config) def __update_state(self): - update_state = self.subparsers.add_parser( + _ = self.subparsers.add_parser( "update-state", description="Permanently update mepo1 state to current", aliases=mepoconfig.get_command_alias("update-state"), diff --git a/src/mepo/cmdline/stash_parser.py b/src/mepo/cmdline/stash_parser.py index af0bb4a..b6edf7e 100644 --- a/src/mepo/cmdline/stash_parser.py +++ b/src/mepo/cmdline/stash_parser.py @@ -1,7 +1,4 @@ -import argparse - - -class MepoStashArgParser(object): +class MepoStashArgParser: def __init__(self, stash): self.stash = stash.add_subparsers() @@ -48,7 +45,7 @@ def __show(self): ) def __list(self): - stlist = self.stash.add_parser( + _ = self.stash.add_parser( "list", description="List local stashes of all components" ) diff --git a/src/mepo/cmdline/tag_parser.py b/src/mepo/cmdline/tag_parser.py index dfacfc0..cb2e89c 100644 --- a/src/mepo/cmdline/tag_parser.py +++ b/src/mepo/cmdline/tag_parser.py @@ -1,7 +1,4 @@ -import argparse - - -class MepoTagArgParser(object): +class MepoTagArgParser: def __init__(self, tag): self.tag = tag.add_subparsers() diff --git a/tests/test_mepo_commands.py b/tests/test_mepo_commands.py index bb5b8ad..fe0e0d2 100644 --- a/tests/test_mepo_commands.py +++ b/tests/test_mepo_commands.py @@ -110,7 +110,7 @@ def __mepo_status(self, saved_output): def __mepo_restore_state(self): os.chdir(self.__class__.fixture_dir) - with contextlib.redirect_stdout(io.StringIO()) as output: + with contextlib.redirect_stdout(io.StringIO()) as _: mepo_restore_state.run(SimpleNamespace()) self.__mepo_status(self.__class__.output_clone_status) @@ -128,7 +128,7 @@ def test_develop(self): comp_name=["env", "cmake", "fvdycore"], quiet=False, ) - with contextlib.redirect_stdout(io.StringIO()) as output: + with contextlib.redirect_stdout(io.StringIO()) as _: mepo_develop.run(args) self.__mepo_status("output_develop_status.txt") # Clean up @@ -246,7 +246,7 @@ def test_fetch(self): ) with contextlib.redirect_stdout(io.StringIO()) as output: mepo_fetch.run(args) - saved_output = "Fetching \x1b[1;33mFVdycoreCubed_GridComp\x1b[0;0m\n" + saved_output = "Fetching \x1b[33mFVdycoreCubed_GridComp\x1b[0m\n" self.assertEqual(output.getvalue(), saved_output) def test_pull(self): From e6dd2f27ddfa1fc872030969dad3f2e4bbfc069c Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 31 May 2024 17:18:13 -0500 Subject: [PATCH 21/27] Some engineering (#292) * Added pre-commit hook to run Black * Removed unused imports etc. --- .pre-commit-config.yaml | 11 +++++++++++ pyproject.toml | 1 + requirements-dev.lock | 17 +++++++++++++++++ src/mepo/command/changed-files.py | 3 --- src/mepo/command/checkout-if-exists.py | 1 - src/mepo/command/clone.py | 2 +- src/mepo/command/commit.py | 8 +++++--- src/mepo/command/compare.py | 2 +- src/mepo/command/develop.py | 2 +- src/mepo/command/diff.py | 3 --- src/mepo/command/init.py | 2 +- src/mepo/command/reset.py | 8 ++++---- src/mepo/command/restore-state.py | 2 -- src/mepo/command/tag_create.py | 8 +++++--- src/mepo/command/unstage.py | 1 - src/mepo/component.py | 3 --- src/mepo/state.py | 6 ------ src/mepo/utilities/colors.py | 3 +-- 18 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..983fce4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + # Using this mirror lets us use mypyc-compiled black, which is about 2x faster + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.4.2 + hooks: + - id: black + # It is recommended to specify the latest version of Python + # supported by your project here, or alternatively use + # pre-commit's default_language_version, see + # https://pre-commit.com/#top_level-default_language_version + language_version: python3.11 diff --git a/pyproject.toml b/pyproject.toml index ea661cf..a06c00a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dev-dependencies = [ "black>=24.4.2", "pylint>=3.2.0", "flake8>=7.0.0", + "pre-commit>=3.7.1", ] [build-system] diff --git a/requirements-dev.lock b/requirements-dev.lock index 45d9811..84d81cd 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,13 +11,21 @@ astroid==3.2.1 # via pylint black==24.4.2 +cfgv==3.4.0 + # via pre-commit click==8.1.7 # via black colorama==0.4.6 # via mepo dill==0.3.8 # via pylint +distlib==0.3.8 + # via virtualenv +filelock==3.14.0 + # via virtualenv flake8==7.0.0 +identify==2.5.36 + # via pre-commit isort==5.13.2 # via pylint mccabe==0.7.0 @@ -27,6 +35,8 @@ mdutils==1.6.0 # via mepo mypy-extensions==1.0.0 # via black +nodeenv==1.8.0 + # via pre-commit packaging==24.0 # via black pathspec==0.12.1 @@ -34,6 +44,8 @@ pathspec==0.12.1 platformdirs==4.2.2 # via black # via pylint + # via virtualenv +pre-commit==3.7.1 pycodestyle==2.11.1 # via flake8 pyflakes==3.2.0 @@ -41,6 +53,7 @@ pyflakes==3.2.0 pylint==3.2.0 pyyaml==6.0.1 # via mepo + # via pre-commit tomli==2.0.1 # via black # via pylint @@ -50,3 +63,7 @@ typing-extensions==4.11.0 # via astroid # via black # via pylint +virtualenv==20.26.2 + # via pre-commit +setuptools==70.0.0 + # via nodeenv diff --git a/src/mepo/command/changed-files.py b/src/mepo/command/changed-files.py index bc8d38c..4d2593a 100644 --- a/src/mepo/command/changed-files.py +++ b/src/mepo/command/changed-files.py @@ -1,10 +1,7 @@ import os -from shutil import get_terminal_size from ..state import MepoState -from ..component import MepoVersion -from ..utilities import colors from ..utilities import verify from ..utilities.version import version_to_string from ..utilities.version import sanitize_version_string diff --git a/src/mepo/command/checkout-if-exists.py b/src/mepo/command/checkout-if-exists.py index ec16258..bb59628 100644 --- a/src/mepo/command/checkout-if-exists.py +++ b/src/mepo/command/checkout-if-exists.py @@ -1,5 +1,4 @@ from ..state import MepoState -from ..utilities import verify from ..git import GitRepository from ..utilities import colors diff --git a/src/mepo/command/clone.py b/src/mepo/command/clone.py index fa3336a..66718a2 100644 --- a/src/mepo/command/clone.py +++ b/src/mepo/command/clone.py @@ -71,7 +71,7 @@ def run(args): if passed_in_registry: try: shutil.copy(args.registry, os.getcwd()) - except shutil.SameFileError as e: + except shutil.SameFileError: pass # This tries to read the state and if not, calls init, diff --git a/src/mepo/command/commit.py b/src/mepo/command/commit.py index 3175ead..d56768a 100644 --- a/src/mepo/command/commit.py +++ b/src/mepo/command/commit.py @@ -1,3 +1,7 @@ +import os +import tempfile +import subprocess + from ..state import MepoState from ..utilities import verify from ..git import GitRepository @@ -5,9 +9,6 @@ from .stage import stage_files -# Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 -import os, tempfile, subprocess - def run(args): allcomps = MepoState.read_state() @@ -17,6 +18,7 @@ def run(args): tf_file = None # Pop up an editor if a message is not provided + # Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 if not args.message: EDITOR = get_git_editor() initial_message = b"" # set up the file diff --git a/src/mepo/command/compare.py b/src/mepo/command/compare.py index b4f737d..84fe6da 100644 --- a/src/mepo/command/compare.py +++ b/src/mepo/command/compare.py @@ -15,7 +15,7 @@ def run(args): allcomps = MepoState.read_state() if not any_differing_repos(allcomps): - print(f"No repositories have changed") + print("No repositories have changed") else: max_namelen, max_origlen = calculate_header_lengths(allcomps, args.all) print_header(max_namelen, max_origlen) diff --git a/src/mepo/command/develop.py b/src/mepo/command/develop.py index eb61127..e679955 100644 --- a/src/mepo/command/develop.py +++ b/src/mepo/command/develop.py @@ -21,4 +21,4 @@ def run(args): ) ) git.checkout(comp.develop) - output = git.pull() + _ = git.pull() diff --git a/src/mepo/command/diff.py b/src/mepo/command/diff.py index 118d98f..e32de83 100644 --- a/src/mepo/command/diff.py +++ b/src/mepo/command/diff.py @@ -1,6 +1,3 @@ -import sys -import time -import multiprocessing as mp import os from shutil import get_terminal_size diff --git a/src/mepo/command/init.py b/src/mepo/command/init.py index b28eb7f..83d15e3 100644 --- a/src/mepo/command/init.py +++ b/src/mepo/command/init.py @@ -17,7 +17,7 @@ def run(args): else: style = None - allcomps = MepoState.initialize(args.registry, style) + _ = MepoState.initialize(args.registry, style) if not style: print(f"Initializing mepo using {args.registry}") diff --git a/src/mepo/command/reset.py b/src/mepo/command/reset.py index 1f36025..87cfce6 100644 --- a/src/mepo/command/reset.py +++ b/src/mepo/command/reset.py @@ -32,7 +32,7 @@ def run(args): # will ask them to confirm that they want to reset the project if not args.force and not args.dry_run: print( - f"Are you sure you want to reset the project? If so, type 'yes' and press enter.", + "Are you sure you want to reset the project? If so, type 'yes' and press enter.", end=" ", ) answer = input() @@ -58,12 +58,12 @@ def run(args): print(f"dry-run only. Not removing {relpath}") # Next, we need to remove the .mepo directory - print(f"Removing mepo state", end="...") + print("Removing mepo state", end="...") if not args.dry_run: shutil.rmtree(MepoState.get_dir()) print("done.") else: - print(f"dry-run only. Not removing mepo state") + print("dry-run only. Not removing mepo state") # If they pass in the --reclone flag, then we will re-clone all the subrepos if args.reclone: @@ -88,7 +88,7 @@ def run(args): mepo_clone_run(clone_args) print("Recloning done.") else: - print(f"Dry-run only. Not re-cloning all subrepos") + print("Dry-run only. Not re-cloning all subrepos") def _get_relative_path(local_path): diff --git a/src/mepo/command/restore-state.py b/src/mepo/command/restore-state.py index e4bd34e..c08aac1 100644 --- a/src/mepo/command/restore-state.py +++ b/src/mepo/command/restore-state.py @@ -1,5 +1,3 @@ -import sys -import time import multiprocessing as mp from ..state import MepoState diff --git a/src/mepo/command/tag_create.py b/src/mepo/command/tag_create.py index 427cc5e..b54ffc8 100644 --- a/src/mepo/command/tag_create.py +++ b/src/mepo/command/tag_create.py @@ -1,11 +1,12 @@ +import os +import tempfile +import subprocess + from ..state import MepoState from ..utilities import verify from ..git import GitRepository from ..git import get_editor as get_git_editor -# Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 -import os, tempfile, subprocess - def run(args): allcomps = MepoState.read_state() @@ -22,6 +23,7 @@ def run(args): if create_annotated_tag: # Pop up an editor if a message is not provided + # Popping up an EDITOR is based on https://stackoverflow.com/a/39989442 if not args.message: EDITOR = get_git_editor() initial_message = b"" # set up the file diff --git a/src/mepo/command/unstage.py b/src/mepo/command/unstage.py index f8ffd91..9bc661c 100644 --- a/src/mepo/command/unstage.py +++ b/src/mepo/command/unstage.py @@ -1,5 +1,4 @@ from ..state import MepoState -from ..utilities import shellcmd from ..utilities import verify from ..git import GitRepository diff --git a/src/mepo/component.py b/src/mepo/component.py index b177227..ef732ba 100644 --- a/src/mepo/component.py +++ b/src/mepo/component.py @@ -1,12 +1,9 @@ import os import shlex -import textwrap -from collections import namedtuple from urllib.parse import urlparse from .utilities import shellcmd -from .utilities import mepoconfig from .utilities.version import MepoVersion # This will be used to store the "final nodes" from each subrepo diff --git a/src/mepo/state.py b/src/mepo/state.py index 827c10e..a6d131f 100644 --- a/src/mepo/state.py +++ b/src/mepo/state.py @@ -5,16 +5,12 @@ import stat import pickle -from pathlib import Path - from .registry import Registry from .component import MepoComponent -from .utilities import shellcmd from .utilities import colors from .utilities.exceptions import StateDoesNotExistError from .utilities.exceptions import StateAlreadyInitializedError from .utilities.chdir import chdir as mepo_chdir -from .utilities.version import MepoVersion class MepoState(object): @@ -98,8 +94,6 @@ def mepo1_patch_undo(): """ Undo changes made my __mepo1_patch(). Called during """ - import mepo - entries_to_remove = ["state", "state.component", "utilities"] for key in entries_to_remove: sys.modules.pop(key, None) diff --git a/src/mepo/utilities/colors.py b/src/mepo/utilities/colors.py index e258168..2a33156 100644 --- a/src/mepo/utilities/colors.py +++ b/src/mepo/utilities/colors.py @@ -1,6 +1,5 @@ try: - import colorama - from colorama import Fore, Back, Style + from colorama import Fore, Style RED = Fore.RED BLUE = Fore.BLUE From fe5695594b95dc95f8880112d453cb08abc5fa8a Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Mon, 3 Jun 2024 16:48:19 -0500 Subject: [PATCH 22/27] Fixed a bug in mepo state creation during 'mepo update-state' was following the codepath for the case when mepo state does not exist. (#295) --- requirements-dev.lock | 4 ++-- src/mepo/state.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 84d81cd..da595f7 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -54,6 +54,8 @@ pylint==3.2.0 pyyaml==6.0.1 # via mepo # via pre-commit +setuptools==70.0.0 + # via nodeenv tomli==2.0.1 # via black # via pylint @@ -65,5 +67,3 @@ typing-extensions==4.11.0 # via pylint virtualenv==20.26.2 # via pre-commit -setuptools==70.0.0 - # via nodeenv diff --git a/src/mepo/state.py b/src/mepo/state.py index a6d131f..6233017 100644 --- a/src/mepo/state.py +++ b/src/mepo/state.py @@ -131,9 +131,15 @@ def __get_new_state_file(cls): if cls.state_exists(): state_dir = cls.get_dir() pattern = os.path.join(cls.get_dir(), "state.*.json") - states = [os.path.basename(x) for x in glob.glob(os.path.join(pattern))] + states = [os.path.basename(x) for x in glob.glob(pattern)] new_state_id = max([int(x.split(".")[1]) for x in states]) + 1 state_filename = "state." + str(new_state_id) + ".json" + elif cls.state_exists(old_style=True): + state_dir = cls.get_dir() + pattern = os.path.join(cls.get_dir(), "state.*.pkl") + states = [os.path.basename(x) for x in glob.glob(pattern)] + new_state_id = max([int(x.split(".")[1]) for x in states]) + state_filename = "state." + str(new_state_id) + ".json" else: state_dir = os.path.join(os.getcwd(), cls.__state_dir_name) os.makedirs(state_dir, exist_ok=True) From 42989f9a35a21220039e9f9e8b448ed9f8a395bc Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Mon, 3 Jun 2024 16:53:01 -0500 Subject: [PATCH 23/27] mdutils is now a dev-dependency instead of dependency (#294) --- pyproject.toml | 2 +- requirements-dev.lock | 1 - requirements.lock | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a06c00a..308021b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,6 @@ description = "A tool for managing (m)ultiple r(epo)s" authors = [{name="GMAO SI Team", email="siteam@gmao.gsfc.nasa.gov"}] dependencies = [ "pyyaml>=6.0.1", - "mdutils>=1.6.0", "colorama>=0.4.6", ] readme = "README.md" @@ -23,6 +22,7 @@ dev-dependencies = [ "pylint>=3.2.0", "flake8>=7.0.0", "pre-commit>=3.7.1", + "mdutils>=1.6.0", ] [build-system] diff --git a/requirements-dev.lock b/requirements-dev.lock index da595f7..15f19a7 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -32,7 +32,6 @@ mccabe==0.7.0 # via flake8 # via pylint mdutils==1.6.0 - # via mepo mypy-extensions==1.0.0 # via black nodeenv==1.8.0 diff --git a/requirements.lock b/requirements.lock index 70e68bb..7cecfb7 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,7 +10,5 @@ -e file:. colorama==0.4.6 # via mepo -mdutils==1.6.0 - # via mepo pyyaml==6.0.1 # via mepo From eb1eab41cc03d0955888e8ade617c2bc1863be7a Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Mon, 10 Jun 2024 17:00:47 -0500 Subject: [PATCH 24/27] A bug fix and '--serial' option for status and restore-state commands (#296) * Another, hopefully last, bug with old-style state fixed - Convert the local path of the repos from relative to absolute after reading the state, in case of old-style state as well * Running 'mepo status' on a mac seems to cause issues for the case of an old style state. Somehow the patch to read the old style state, modification of sys.modules, does not percolate down to the processes spawned by multiprocessing. To work around this issue, added a '--serial' option to the status command. * Same issue with 'mepo restore-state' as we experienced with 'mepo status' earlier (81020ef). To work around this issue, added a '--serial' option. * Updated test to account for new option (--serial) for status and restore-state --- src/mepo/cmdline/parser.py | 8 +++- src/mepo/command/restore-state.py | 9 +++- src/mepo/command/status.py | 74 +++++++++++++++++-------------- src/mepo/state.py | 2 + tests/test_mepo_commands.py | 4 +- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/src/mepo/cmdline/parser.py b/src/mepo/cmdline/parser.py index ee71208..cddd3f2 100644 --- a/src/mepo/cmdline/parser.py +++ b/src/mepo/cmdline/parser.py @@ -150,13 +150,19 @@ def __status(self): status.add_argument( "--hashes", action="store_true", help="Print the exact hash of the HEAD." ) + status.add_argument( + "--serial", action="store_true", help="Run the serial version." + ) def __restore_state(self): - _ = self.subparsers.add_parser( + restore_state = self.subparsers.add_parser( "restore-state", description="Restores all components to the last saved state.", aliases=mepoconfig.get_command_alias("restore-state"), ) + restore_state.add_argument( + "--serial", action="store_true", help="Run the serial version." + ) def __diff(self): diff = self.subparsers.add_parser( diff --git a/src/mepo/command/restore-state.py b/src/mepo/command/restore-state.py index c08aac1..bb33ee4 100644 --- a/src/mepo/command/restore-state.py +++ b/src/mepo/command/restore-state.py @@ -9,8 +9,13 @@ def run(args): print("Checking status...", flush=True) allcomps = MepoState.read_state() - with mp.Pool() as pool: - result = pool.map(check_component_status, allcomps) + if args.serial: + result = [] + for comp in allcomps: + result.append(check_component_status(comp)) + else: + with mp.Pool() as pool: + result = pool.map(check_component_status, allcomps) restore_state(allcomps, result) diff --git a/src/mepo/command/status.py b/src/mepo/command/status.py index a4ee55d..36db38a 100644 --- a/src/mepo/command/status.py +++ b/src/mepo/command/status.py @@ -18,12 +18,19 @@ def run(args): """Entry point""" print("Checking status...", flush=True) allcomps = MepoState.read_state() - with mp.Pool() as pool: - result = pool.starmap( - check_component_status, - [(comp, args.ignore_permissions) for comp in allcomps], - ) - print_status(allcomps, result, args.nocolor, args.hashes) + # max_width = len(max([comp.name for comp in allcomps], key=len)) + max_width = max([len(comp.name) for comp in allcomps]) + if args.serial: + for comp in allcomps: + result = check_component_status(comp, args.ignore_permissions) + print_component_status(comp, result, max_width, args.nocolor, args.hashes) + else: + with mp.Pool() as pool: + result = pool.starmap( + check_component_status, + [(comp, args.ignore_permissions) for comp in allcomps], + ) + print_status(allcomps, result, max_width, args.nocolor, args.hashes) def check_component_status(comp, ignore_permissions): @@ -54,32 +61,33 @@ def check_component_status(comp, ignore_permissions): ) -def print_status(allcomps, result, nocolor=False, hashes=False): - orig_width = len(max([comp.name for comp in allcomps], key=len)) +def print_status(allcomps, result, max_width, nocolor=False, hashes=False): for index, comp in enumerate(allcomps): time.sleep(0.025) - current_version, internal_state_branch_name, output = result[index] - if hashes: - comp_path = _get_relative_path(comp.local) - comp_hash = shellcmd.run( - cmd=shlex.split(f"git -C {comp_path} rev-parse HEAD"), output=True - ).replace("\n", "") - current_version = f"{current_version} ({comp_hash})" - # This should handle tag weirdness... - if current_version.split()[1] == comp.version.name: - component_name = comp.name - width = orig_width - # Check to see if the current tag/branch is the same as the - # original... if the above check didn't succeed, we are - # different and we colorize if asked for - elif (internal_state_branch_name not in comp.version.name) and not nocolor: - component_name = colors.RED + comp.name + colors.RESET - width = orig_width + len(colors.RED) + len(colors.RESET) - else: - component_name = comp.name - width = orig_width - FMT0 = "{:<%s.%ss} | {: Date: Wed, 26 Jun 2024 09:49:57 -0500 Subject: [PATCH 25/27] Version 2.0.0rc4 (#297) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 308021b..aed31f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mepo" -version = "2.0.0" +version = "2.0.0rc4" description = "A tool for managing (m)ultiple r(epo)s" authors = [{name="GMAO SI Team", email="siteam@gmao.gsfc.nasa.gov"}] dependencies = [ From d2585a1d61aa112f64605bda130227a5a43daf3d Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Fri, 9 Aug 2024 10:06:02 -0400 Subject: [PATCH 26/27] Add docs README, update main README (#272) --- .gitignore | 3 +++ CHANGELOG.md | 1 + 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 3e8c1eb..07e88d2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ *.egg-info dist venv + +# This is generated by docs/make_md_docs.py +Mepo-Commands.md .python-version diff --git a/CHANGELOG.md b/CHANGELOG.md index f35af84..564e346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 -- More code reorganization - Helper script `mepo`, used for development, moved to the `bin` directory. +- Added README for `docs/make_md_docs.py` script - State: pickle format (mepo1 style) to json format (mepo2 style) -- If mepo1 style state is detected, print warning and suggest running `mepo update-state` From 0da5ef49c85c9fbb3d960d0b980a039f1a779479 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Fri, 9 Aug 2024 09:57:11 -0500 Subject: [PATCH 27/27] Ready to release 2.0.0 (#298) * Ready to release 2.0.0 * Updated CHANGELOG.md --------- Co-authored-by: Matt Thompson --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++++ pyproject.toml | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 564e346..8f0d8d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +## [2.0.0] - 2024-08-09 + +### Fixed + +### Added + - Added `pyproject.toml` to aid with `pip` installation. - Engineering diff --git a/README.md b/README.md index d7c0192..5bf8a88 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ `mepo` is a tool, written in Python3 (3.9.0+), to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. +## Installation + +`pip install mepo` + ## Commands For more about the possible `mepo` commands, please see the [Mepo Commands](https://github.com/GEOS-ESM/mepo/wiki/Mepo-Commands) wiki page. diff --git a/pyproject.toml b/pyproject.toml index aed31f7..308021b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mepo" -version = "2.0.0rc4" +version = "2.0.0" description = "A tool for managing (m)ultiple r(epo)s" authors = [{name="GMAO SI Team", email="siteam@gmao.gsfc.nasa.gov"}] dependencies = [