Convert Blender-Purge
to a more generic Blender-Crawl
Tool
#42
@ -1,25 +0,0 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
"""Top-level package for blender_purge."""
|
||||
|
||||
__author__ = """Paul Golter"""
|
||||
__email__ = "paul@blender.org"
|
||||
__version__ = "0.1.0"
|
@ -19,37 +19,68 @@
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
|
||||
# PARSE ARUGMENTS
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Tuple, List, Dict, Any, Union, Optional
|
||||
from typing import Tuple, List, Any
|
||||
|
||||
from blender_purge import vars
|
||||
from blender_purge.svn import SvnRepo
|
||||
from blender_purge.log import LoggerFactory
|
||||
from blender_purge.exception import SomethingWentWrongException, WrongInputException
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
from pathlib import Path
|
||||
|
||||
# Command line arguments.
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"path", help="Path to a file or folder on which to perform purge", type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"-R",
|
||||
"--recursive",
|
||||
help="If -R is provided in combination with a folder path will perform recursive purge",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--regex",
|
||||
help="Provide any regex pattern that will be performed on each found filepath with re.search()",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--yes",
|
||||
help="If --yes is provided there will be no confirmation prompt.",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
# MAIN LOGIC
|
||||
PURGE_PATH = Path(os.path.abspath(__file__)).parent.joinpath("purge.py")
|
||||
|
||||
PURGE_AMOUNT = 2
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
run_blender_purge(args)
|
||||
|
||||
def exception_handler(func):
|
||||
def func_wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
except WrongInputException as error:
|
||||
logger.info(
|
||||
except Exception as error:
|
||||
print(
|
||||
"# Oops. Seems like you gave some wrong input!"
|
||||
f"\n# Error: {error}"
|
||||
"\n# Program will be cancelled."
|
||||
)
|
||||
cancel_program()
|
||||
|
||||
except SomethingWentWrongException as error:
|
||||
logger.info(
|
||||
except Exception as error:
|
||||
print(
|
||||
"# Oops. Something went wrong during the execution of the Program!"
|
||||
f"\n# Error: {error}"
|
||||
"\n# Program will be cancelled."
|
||||
@ -60,7 +91,7 @@ def exception_handler(func):
|
||||
|
||||
|
||||
def cancel_program() -> None:
|
||||
logger.info("# Exiting blender-purge")
|
||||
print("# Exiting blender-purge")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@ -82,7 +113,7 @@ def get_cmd_list(path: Path) -> Tuple[str]:
|
||||
path.as_posix(),
|
||||
"-b",
|
||||
"-P",
|
||||
f"{vars.PURGE_PATH}",
|
||||
f"{PURGE_PATH}",
|
||||
"--factory-startup",
|
||||
)
|
||||
return cmd_list
|
||||
@ -105,25 +136,14 @@ def prompt_confirm(path_list: List[Path]):
|
||||
user_input = input(input_str)
|
||||
if validate_user_input(user_input, options):
|
||||
if user_input in ["no", "n"]:
|
||||
logger.info("\n# Process was canceled.")
|
||||
print("\n# Process was canceled.")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
logger.info("\n# Please enter a valid answer!")
|
||||
print("\n# Please enter a valid answer!")
|
||||
continue
|
||||
|
||||
|
||||
def run_check():
|
||||
cmd_list: Tuple[str] = (
|
||||
get_blender_path().as_posix(),
|
||||
"-b",
|
||||
"-P",
|
||||
f"{vars.CHECK_PATH}",
|
||||
)
|
||||
p = subprocess.Popen(cmd_list)
|
||||
return p.wait()
|
||||
|
||||
|
||||
def purge_file(path: Path) -> int:
|
||||
# Get cmd list.
|
||||
cmd_list = get_cmd_list(path)
|
||||
@ -136,11 +156,11 @@ def is_filepath_valid(path: Path) -> None:
|
||||
|
||||
# Check if path is file.
|
||||
if not path.is_file():
|
||||
raise WrongInputException(f"Not a file: {path.suffix}")
|
||||
raise Exception(f"Not a file: {path.suffix}")
|
||||
|
||||
# Check if path is blend file.
|
||||
if path.suffix != ".blend":
|
||||
raise WrongInputException(f"Not a blend file: {path.suffix}")
|
||||
raise Exception(f"Not a blend file: {path.suffix}")
|
||||
|
||||
|
||||
def get_config_path() -> Path:
|
||||
@ -161,11 +181,11 @@ def create_config_file(config_path: Path) -> None:
|
||||
with open(config_path.as_posix(), "w") as file:
|
||||
json.dump({}, file)
|
||||
except:
|
||||
raise SomethingWentWrongException(
|
||||
raise Exception(
|
||||
f"# Something went wrong creating config file at: {config_path.as_posix()}"
|
||||
)
|
||||
|
||||
logger.info(f"# Created config file at: {config_path.as_posix()}")
|
||||
print(f"# Created config file at: {config_path.as_posix()}")
|
||||
|
||||
|
||||
def load_json(path: Path) -> Any:
|
||||
@ -185,12 +205,12 @@ def input_path(question: str) -> Path:
|
||||
try:
|
||||
path = Path(user_input)
|
||||
except:
|
||||
logger.error("# Invalid input")
|
||||
print("ERROR:# Invalid input")
|
||||
continue
|
||||
if path.exists():
|
||||
return path.absolute()
|
||||
else:
|
||||
logger.info("# Path does not exist")
|
||||
print("# Path does not exist")
|
||||
|
||||
|
||||
def input_filepath(question: str) -> Path:
|
||||
@ -211,7 +231,7 @@ def setup_config() -> None:
|
||||
"project_root": project_root.as_posix(),
|
||||
}
|
||||
save_to_json(obj, config_path)
|
||||
logger.info("Updatet config at: %s", config_path.as_posix())
|
||||
print("Updatet config at: %s", config_path.as_posix())
|
||||
|
||||
|
||||
def is_config_valid() -> bool:
|
||||
@ -227,33 +247,32 @@ def is_config_valid() -> bool:
|
||||
|
||||
|
||||
@exception_handler
|
||||
def purge(args: argparse.Namespace) -> int:
|
||||
def run_blender_purge(args: argparse.Namespace) -> int:
|
||||
|
||||
# Parse arguments.
|
||||
path = Path(args.path).absolute()
|
||||
recursive = args.recursive
|
||||
config_path = get_config_path()
|
||||
no_commit = args.nocommit
|
||||
regex = args.regex
|
||||
yes = args.yes
|
||||
|
||||
# Check config file.
|
||||
if not config_path.exists():
|
||||
logger.info("# Seems like you are starting blender-purge for the first time!")
|
||||
logger.info("# Some things needs to be configured")
|
||||
print("# Seems like you are starting blender-purge for the first time!")
|
||||
print("# Some things needs to be configured")
|
||||
setup_config()
|
||||
else:
|
||||
if not is_config_valid():
|
||||
logger.info("# Config file at: %s is not valid", config_path.as_posix())
|
||||
logger.info("# Please set it up again")
|
||||
print("# Config file at: %s is not valid", config_path.as_posix())
|
||||
print("# Please set it up again")
|
||||
setup_config()
|
||||
|
||||
# Check user input.
|
||||
if not path:
|
||||
raise WrongInputException("Please provide a path as first argument")
|
||||
raise Exception("Please provide a path as first argument")
|
||||
|
||||
if not path.exists():
|
||||
raise WrongInputException(f"Path does not exist: {path.as_posix()}")
|
||||
raise Exception(f"Path does not exist: {path.as_posix()}")
|
||||
|
||||
# Vars.
|
||||
files = []
|
||||
@ -288,7 +307,7 @@ def purge(args: argparse.Namespace) -> int:
|
||||
|
||||
# Can only happen on folder here.
|
||||
if not files:
|
||||
logger.info("# Found no .blend files to purge")
|
||||
print("# Found no .blend files to purge")
|
||||
cancel_program()
|
||||
|
||||
# Sort.
|
||||
@ -299,30 +318,21 @@ def purge(args: argparse.Namespace) -> int:
|
||||
if not prompt_confirm(files):
|
||||
cancel_program()
|
||||
|
||||
"""
|
||||
# Perform check of correct preference settings.
|
||||
return_code = run_check()
|
||||
if return_code == 1:
|
||||
raise SomethingWentWrongException(
|
||||
"Override auto resync is turned off. Turn it on in the preferences and try again."
|
||||
)
|
||||
"""
|
||||
|
||||
# Purge each file two times.
|
||||
for blend_file in files:
|
||||
for i in range(vars.PURGE_AMOUNT):
|
||||
for i in range(PURGE_AMOUNT):
|
||||
return_code = purge_file(blend_file)
|
||||
if return_code != 0:
|
||||
raise SomethingWentWrongException(
|
||||
raise Exception(
|
||||
f"Blender Crashed on file: {blend_file.as_posix()}",
|
||||
)
|
||||
|
||||
# Commit to svn.
|
||||
if no_commit:
|
||||
return 0
|
||||
|
||||
project_root = get_project_root_path()
|
||||
svn_repo = SvnRepo(project_root)
|
||||
file_rel = [p.relative_to(project_root) for p in files]
|
||||
svn_repo.commit(file_rel)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
@ -1,32 +0,0 @@
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
import sys
|
||||
import bpy
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Check if recursive is on.
|
||||
if not bpy.context.preferences.experimental.override_auto_resync:
|
||||
logger.error("Override auto resync is turned off!")
|
||||
sys.exit(1)
|
@ -1,72 +0,0 @@
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import importlib
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from blender_purge import app
|
||||
from blender_purge.log import LoggerFactory
|
||||
|
||||
importlib.reload(app)
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
# Command line arguments.
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"path", help="Path to a file or folder on which to perform purge", type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"-R",
|
||||
"--recursive",
|
||||
help="If -R is provided in combination with a folder path will perform recursive purge",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-N",
|
||||
"--nocommit",
|
||||
help="If -N is provided there will be no svn commit prompt with the purged files.",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--regex",
|
||||
help="Provide any regex pattern that will be performed on each found filepath with re.search()",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--yes",
|
||||
help="If --yes is provided there will be no confirmation prompt.",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
app.purge(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,27 +0,0 @@
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
class WrongInputException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SomethingWentWrongException(Exception):
|
||||
pass
|
@ -1,42 +0,0 @@
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
||||
class LoggerFactory:
|
||||
|
||||
"""
|
||||
Utility class to streamline logger creation
|
||||
"""
|
||||
|
||||
formatter = logging.Formatter("%(name)s: %(message)s")
|
||||
consoleHandler = logging.StreamHandler(sys.stdout)
|
||||
level = logging.INFO
|
||||
|
||||
@classmethod
|
||||
def getLogger(cls, name=__name__):
|
||||
logger = logging.getLogger(name)
|
||||
logger.addHandler(cls.consoleHandler)
|
||||
logger.setLevel(cls.level)
|
||||
|
||||
return logger
|
@ -1,118 +0,0 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
from typing import List, Union, Tuple, Any, Dict, Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class SvnRepo:
|
||||
def __init__(self, path: Path) -> None:
|
||||
self._orig_pwd = Path(os.path.abspath(os.getcwd()))
|
||||
self._path = path.absolute()
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return self._path
|
||||
|
||||
def status(self) -> List[str]:
|
||||
output = str(
|
||||
subprocess.check_output(
|
||||
["svn status"], shell=True, cwd=self._path.as_posix()
|
||||
),
|
||||
"utf-8",
|
||||
)
|
||||
# Split output in string lines.
|
||||
split = output.split("\n")
|
||||
|
||||
# Remove empty lines.
|
||||
while True:
|
||||
try:
|
||||
split.remove("")
|
||||
except ValueError:
|
||||
break
|
||||
return split
|
||||
|
||||
def get_modified(self, suffix: str = ".*") -> List[Path]:
|
||||
output = self.status()
|
||||
if not output:
|
||||
return []
|
||||
|
||||
path_list: List[Path] = []
|
||||
|
||||
# Assemble path list.
|
||||
for idx, line in enumerate(output):
|
||||
if not line.startswith("M"):
|
||||
continue
|
||||
path = Path(line[5:].strip())
|
||||
|
||||
# If no suffix supplied append all files.
|
||||
if suffix == ".*":
|
||||
path_list.append(path)
|
||||
# If suffix supplied only collect files that match.
|
||||
else:
|
||||
if path.suffix == suffix:
|
||||
path_list.append(path)
|
||||
|
||||
return path_list
|
||||
|
||||
def revert(self, relpath_list: List[Path]) -> subprocess.Popen:
|
||||
arg_list = " ".join([p.as_posix() for p in relpath_list])
|
||||
process = subprocess.call(
|
||||
(f"svn revert {arg_list}"), shell=True, cwd=self._path.as_posix()
|
||||
)
|
||||
return process
|
||||
|
||||
def revert_all(self) -> None:
|
||||
modified = self.get_modified()
|
||||
self.revert(modified)
|
||||
|
||||
def commit(self, relpath_list: List[Path]) -> Optional[subprocess.Popen]:
|
||||
if not relpath_list:
|
||||
return None
|
||||
|
||||
cmd_list = f'svn commit {" ".join([p.as_posix() for p in relpath_list])}'
|
||||
process = subprocess.call(cmd_list, shell=True, cwd=self._path.as_posix())
|
||||
return process
|
||||
|
||||
def get_untracked(self) -> List[Path]:
|
||||
output = self.status()
|
||||
if not output:
|
||||
return []
|
||||
|
||||
path_list: List[Path] = []
|
||||
|
||||
# Assemble path list.
|
||||
for idx, line in enumerate(output):
|
||||
if not line.startswith("?"):
|
||||
continue
|
||||
path = Path(line[5:].strip())
|
||||
path_list.append(path)
|
||||
|
||||
return path_list
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test status.
|
||||
repo = SvnRepo(Path("/media/data/sprites"))
|
||||
modified = repo.get_modified()
|
||||
print(modified)
|
||||
repo.commit(modified[:2])
|
@ -1,29 +0,0 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
#
|
||||
# (c) 2021, Blender Foundation
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
PURGE_PATH = Path(os.path.abspath(__file__)).parent.joinpath("purge.py")
|
||||
CHECK_PATH = Path(os.path.abspath(__file__)).parent.joinpath("check.py")
|
||||
|
||||
BLENDER_PATH = "/media/data/blender_guest/cmake_release/bin/blender"
|
||||
PROJECT_PATH = "/media/data/sprites"
|
||||
PURGE_AMOUNT = 2
|
@ -42,5 +42,5 @@ setup(
|
||||
tests_require=test_requirements,
|
||||
version="0.1.0",
|
||||
zip_safe=False,
|
||||
entry_points={"console_scripts": ["bpurge=blender_purge.cli:main"]},
|
||||
entry_points={"console_scripts": ["bpurge = blender_purge.__main__:main"]},
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user