Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove configurable workspace directory #714

Merged
merged 8 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 4 additions & 29 deletions signac/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
else:
READLINE = True

from . import Project, get_project, init_project
from . import get_project, init_project
from .common import config
from .common.configobj import Section, flatten_errors
from .contrib.filterparse import _add_prefix, parse_filter_arg
Expand Down Expand Up @@ -74,7 +74,6 @@
signac {signac_version} 🎨

Project:\t{root_path}{job_banner}
Workspace:\t{workspace_path}
Size:\t\t{size}

Interact with the project interface using the "project" or "pr" variable.
Expand Down Expand Up @@ -343,7 +342,7 @@ def main_view(args):

def main_init(args):
"""Handle init subcommand."""
init_project(name=args.project_id, root=os.getcwd(), workspace=args.workspace)
init_project(name=args.project_id, root=os.getcwd())
_print_err("Initialized project.")


Expand Down Expand Up @@ -411,16 +410,8 @@ def _sig(st):
try:
destination = get_project(root=args.destination)
except LookupError:
if args.allow_workspace:
# TODO: Remove allow_workspace entirely.
destination = Project(os.path.relpath(args.destination))
else:
_print_err(
"WARNING: The destination appears to not be a project path. "
"Use the '-w/--allow-workspace' option if you want to "
"synchronize to a workspace directory directly."
)
raise
_print_err("WARNING: The destination does not appear to be a project path.")
raise
selection = find_with_filter_or_none(args)

if args.strategy:
Expand Down Expand Up @@ -548,7 +539,6 @@ def _main_import_interactive(project, origin, args):
signac_version=__version__,
job_banner="",
root_path=project.root_directory(),
workspace_path=project.workspace(),
size=len(project),
origin=args.origin,
),
Expand Down Expand Up @@ -890,7 +880,6 @@ def write_history_file():
signac_version=__version__,
job_banner=f"\nJob:\t\t{job.id}" if job is not None else "",
root_path=project.root_directory(),
workspace_path=project.workspace(),
size=len(project),
),
)
Expand Down Expand Up @@ -919,13 +908,6 @@ def main():

parser_init = subparsers.add_parser("init")
parser_init.add_argument("project_id", nargs="?", help=argparse.SUPPRESS)
parser_init.add_argument(
"-w",
"--workspace",
type=str,
default="workspace",
help="The path to the workspace directory.",
)
parser_init.set_defaults(func=main_init)

parser_project = subparsers.add_parser("project")
Expand Down Expand Up @@ -1465,13 +1447,6 @@ def main():
"--no-keys", action="store_true", help="Never overwrite any conflicting keys."
)

parser_sync.add_argument(
"-w",
"--allow-workspace",
action="store_true",
help="Allow the specification of a workspace (instead of a project) directory "
"as the destination path.",
)
parser_sync.add_argument(
"--force", action="store_true", help="Ignore all warnings, just synchronize."
)
Expand Down
1 change: 0 additions & 1 deletion signac/common/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ def get_validator(): # noqa: D103

# TODO: Rename to something internal and uppercase e.g. _CFG.
cfg = """
workspace_dir = string(default='workspace')
schema_version = string(default='1')
"""
21 changes: 19 additions & 2 deletions signac/contrib/migration/v1_to_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
# A minimal v2 config.
_cfg = """
schema_version = string(default='0')
workspace_dir = string(default='workspace')
"""


Expand All @@ -37,10 +36,28 @@ def _load_config_v2(root_directory):

def _migrate_v1_to_v2(root_directory):
"""Migrate from schema version 1 to version 2."""
# Delete project name from config and store in project doc.
# Load the v1 config.
cfg = _load_config_v1(root_directory)
fn_doc = os.path.join(root_directory, Project.FN_DOCUMENT)
doc = BufferedJSONAttrDict(filename=fn_doc, write_concern=True)

# Try to migrate a custom workspace directory if one exists.
current_workspace_name = cfg.get("workspace_dir")
if current_workspace_name is not None:
if current_workspace_name != "workspace":
current_workspace = os.path.join(root_directory, current_workspace_name)
new_workspace = os.path.join(root_directory, "workspace")
if os.path.exists(new_workspace):
raise RuntimeError(
"Workspace directories are no longer configurable in schema version 2, and "
f"must be 'workspace', but {new_workspace} already exists. Please remove or "
f"move it so that the currently configured workspace directory "
f"{current_workspace} can be moved to {new_workspace}."
)
os.replace(current_workspace, new_workspace)
del cfg["workspace_dir"]

# Delete project name from config and store in project doc.
doc["signac_project_name"] = cfg["project"]
del cfg["project"]
cfg.write()
Expand Down
52 changes: 6 additions & 46 deletions signac/contrib/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ def __init__(self, root=None):
# Prepare root directory and workspace paths.
# os.path is used instead of pathlib.Path for performance.
self._root_directory = os.path.abspath(root)
self._workspace = os.path.expandvars(
self.config.get("workspace_dir", "workspace")
)
if not os.path.isabs(self._workspace):
self._workspace = os.path.join(self._root_directory, self._workspace)
self._workspace = os.path.join(self._root_directory, "workspace")

# Prepare workspace directory.
if not os.path.isdir(self.workspace()):
Expand Down Expand Up @@ -233,16 +229,6 @@ def root_directory(self):
def workspace(self):
"""Return the project's workspace directory.

The workspace defaults to `project_root/workspace`. Configure this
directory with the ``'workspace_dir'`` configuration option. A relative
path is assumed to be relative to the project's root directory.

.. note::
The configuration will respect environment variables,
such as ``$HOME``.

See :ref:`signac project -w <signac-cli-project>` for the command line equivalent.

Returns
-------
str
Expand Down Expand Up @@ -1474,7 +1460,7 @@ def temporary_project(self, dir=None):
yield tmp_project

@classmethod
def init_project(cls, *args, root=None, workspace=None, make_dir=True, **kwargs):
def init_project(cls, *args, root=None, **kwargs):
"""Initialize a project in the provided root directory.

It is safe to call this function multiple times with the same
Expand All @@ -1489,12 +1475,6 @@ def init_project(cls, *args, root=None, workspace=None, make_dir=True, **kwargs)
root : str, optional
The root directory for the project.
Defaults to the current working directory.
workspace : str, optional
The workspace directory for the project.
Defaults to a subdirectory ``workspace`` in the project root.
make_dir : bool, optional
Create the project root directory if it does not exist yet
(Default value = True).

Returns
-------
Expand Down Expand Up @@ -1565,26 +1545,14 @@ def init_project(cls, *args, root=None, workspace=None, make_dir=True, **kwargs)
)
except LookupError:
fn_config = _get_project_config_fn(root)
if make_dir:
_mkdir_p(os.path.dirname(fn_config))
_mkdir_p(os.path.dirname(fn_config))
config = read_config_file(fn_config)
if workspace is not None:
config["workspace_dir"] = workspace
config["schema_version"] = SCHEMA_VERSION
config.write()
project = cls.get_project(root=root)
if name is not None:
project.doc[name_key] = name
return project
else:
if workspace is not None and os.path.realpath(
workspace
) != os.path.realpath(project.workspace()):
raise RuntimeError(
f"Failed to initialize project. Path '{os.path.abspath(root)}' already "
"contains a conflicting project configuration."
)
return project
return project

@classmethod
def get_project(cls, root=None, search=True, **kwargs):
Expand Down Expand Up @@ -2147,7 +2115,7 @@ def _repr_html_(self):
return repr(self) + self._repr_html_jobs()


def init_project(*args, root=None, workspace=None, make_dir=True, **kwargs):
def init_project(*args, root=None, **kwargs):
"""Initialize a project.

It is safe to call this function multiple times with the same arguments.
Expand All @@ -2159,12 +2127,6 @@ def init_project(*args, root=None, workspace=None, make_dir=True, **kwargs):
root : str, optional
The root directory for the project.
Defaults to the current working directory.
workspace : str, optional
The workspace directory for the project.
Defaults to a subdirectory ``workspace`` in the project root.
make_dir : bool, optional
Create the project root directory, if it does not exist yet (Default
value = True).

Returns
-------
Expand All @@ -2178,9 +2140,7 @@ def init_project(*args, root=None, workspace=None, make_dir=True, **kwargs):
configuration.

"""
return Project.init_project(
*args, root=root, workspace=workspace, make_dir=make_dir, **kwargs
)
return Project.init_project(*args, root=root, **kwargs)


def get_project(root=None, search=True, **kwargs):
Expand Down
5 changes: 1 addition & 4 deletions tests/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,9 @@ def setUp(self, request):
self._tmp_dir = TemporaryDirectory(prefix="signac_")
request.addfinalizer(self._tmp_dir.cleanup)
self._tmp_pr = os.path.join(self._tmp_dir.name, "pr")
self._tmp_wd = os.path.join(self._tmp_dir.name, "wd")
os.mkdir(self._tmp_pr)
self.config = signac.common.config.load_config()
self.project = self.project_class.init_project(
root=self._tmp_pr, workspace=self._tmp_wd
)
self.project = self.project_class.init_project(root=self._tmp_pr)

def tearDown(self):
pass
Expand Down
Loading