pyrecli 0.1.2__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyrecli
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Command line utilities for DiamondFire templates
5
5
  Home-page: https://github.com/Amp63/pyrecli
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyrecli"
3
- version = "0.1.2"
3
+ version = "0.2.0"
4
4
  description = "Command line utilities for DiamondFire templates"
5
5
  authors = ["Amp"]
6
6
  readme = "README.md"
@@ -1,8 +1,8 @@
1
1
  from typing import Literal, TypedDict
2
- from result import Result, Ok, Err
2
+ from result import Result, Err
3
3
  from dfpyre import DFTemplate, Item, Parameter
4
4
  from mcitemlib.itemlib import MCItemlibException
5
- from pyrecli.util import parse_templates_from_file
5
+ from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
6
6
 
7
7
 
8
8
  STARTER_BLOCK_LOOKUP = {
@@ -25,7 +25,11 @@ class TemplateDocData(TypedDict):
25
25
 
26
26
 
27
27
  def docs_command(input_path: str, output_path: str, title: str, include_hidden: bool, omit_toc: bool) -> Result[None, str]:
28
- templates_result = parse_templates_from_file(input_path)
28
+ input_result = read_input_file(input_path)
29
+ if input_result.is_err():
30
+ return Err(input_result.err_value)
31
+
32
+ templates_result = parse_templates_from_string(input_result.ok_value)
29
33
  if templates_result.is_err():
30
34
  return Err(templates_result.err_value)
31
35
  templates = templates_result.ok_value
@@ -68,7 +72,10 @@ def docs_command(input_path: str, output_path: str, title: str, include_hidden:
68
72
  lore_text = [escape_md(l.to_string()) for l in first_arg.get_lore()]
69
73
  if lore_text:
70
74
  template_doc_lines.extend(lore_text)
71
- except MCItemlibException:
75
+ except (MCItemlibException, AttributeError):
76
+ # There are so many things that can go wrong here due to various legacy
77
+ # item formats and weird MC string edge cases, so we can just skip
78
+ # if there's a problem.
72
79
  pass
73
80
 
74
81
  # Parse parameters
@@ -108,13 +115,10 @@ def docs_command(input_path: str, output_path: str, title: str, include_hidden:
108
115
  add_toc_group(process_templates, 'Processes')
109
116
  output_lines.append('\n')
110
117
 
111
-
112
118
  # Add template docs to output lines
113
119
  for doc_data in template_docs:
114
120
  output_lines.extend(doc_data['doc_lines'])
115
121
  output_lines.append('')
116
122
 
117
- with open(output_path, 'w') as f:
118
- f.write('\n'.join(output_lines))
119
-
120
- return Ok(None)
123
+ write_result = write_output_file(output_path, '\n'.join(output_lines))
124
+ return write_result
@@ -2,7 +2,7 @@ import json
2
2
  from result import Result, Ok, Err
3
3
  import amulet_nbt
4
4
  from amulet_nbt import CompoundTag, StringTag
5
- from pyrecli.util import connect_to_codeclient
5
+ from pyrecli.util import write_output_file, connect_to_codeclient
6
6
 
7
7
 
8
8
  def grabinv_command(output_path: str) -> Result[None, str]:
@@ -42,8 +42,8 @@ def grabinv_command(output_path: str) -> Result[None, str]:
42
42
  if not template_codes:
43
43
  return Err('Could not find any templates in the inventory.')
44
44
 
45
- with open(output_path, 'w') as f:
46
- f.write('\n'.join(template_codes))
45
+ output_result = write_output_file(output_path, '\n'.join(template_codes))
46
+ if output_result.is_err():
47
+ return output_result
47
48
 
48
- print(f'Saved {len(template_codes)} template{"s" if len(template_codes) != 1 else ''} to "{output_path}".')
49
49
  return Ok(None)
@@ -1,8 +1,8 @@
1
1
  from typing import Literal
2
2
  import re
3
- from result import Result, Ok, Err
3
+ from result import Result, Err
4
4
  from dfpyre import Variable, Number, String, Text
5
- from pyrecli.util import parse_templates_from_file
5
+ from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
6
6
 
7
7
 
8
8
  TEXT_CODE_PATTERNS = [
@@ -22,8 +22,13 @@ def rename_var_in_text_code(s: str, var_to_rename: str, new_var_name: str):
22
22
 
23
23
  def rename_command(input_path: str, output_path: str|None,
24
24
  var_to_rename: str, new_var_name: str,
25
- renamed_var_scope: Literal['game', 'saved', 'local', 'line']|None) -> Result[None, str]:
26
- templates_result = parse_templates_from_file(input_path)
25
+ var_to_rename_scope: Literal['game', 'saved', 'local', 'line']|None) -> Result[None, str]:
26
+
27
+ input_result = read_input_file(input_path)
28
+ if input_result.is_err():
29
+ return Err(input_result.err_value)
30
+
31
+ templates_result = parse_templates_from_string(input_result.ok_value)
27
32
  if templates_result.is_err():
28
33
  return Err(templates_result.err_value)
29
34
  templates = templates_result.ok_value
@@ -32,11 +37,8 @@ def rename_command(input_path: str, output_path: str|None,
32
37
  for codeblock in template.codeblocks:
33
38
  for argument in codeblock.args:
34
39
  if isinstance(argument, Variable):
35
- if argument.name == var_to_rename:
36
- if renamed_var_scope is None:
37
- argument.name = new_var_name
38
- elif argument.scope != renamed_var_scope:
39
- argument.name = new_var_name
40
+ if argument.name == var_to_rename and (var_to_rename_scope is None or argument.scope == var_to_rename_scope):
41
+ argument.name = new_var_name
40
42
  argument.name = rename_var_in_text_code(argument.name, var_to_rename, new_var_name)
41
43
 
42
44
  elif isinstance(argument, (Number, String, Text)) and isinstance(argument.value, str):
@@ -48,7 +50,5 @@ def rename_command(input_path: str, output_path: str|None,
48
50
 
49
51
  new_file_content = '\n'.join(t.build() for t in templates)
50
52
  write_path = output_path if output_path else input_path
51
- with open(write_path, 'w') as f:
52
- f.write(new_file_content)
53
-
54
- return Ok(None)
53
+ write_result = write_output_file(write_path, new_file_content)
54
+ return write_result
@@ -0,0 +1,19 @@
1
+ from result import Result, Err
2
+ from pyrecli.util import write_output_file, connect_to_codeclient
3
+
4
+
5
+ def scan_command(output_path: str) -> Result[None, str]:
6
+ ws_result = connect_to_codeclient('read_plot')
7
+ if ws_result.is_err():
8
+ return Err(ws_result.err_value)
9
+ ws = ws_result.ok_value
10
+
11
+ print('Scanning plot...')
12
+ ws.send('scan')
13
+
14
+ scan_results = ws.recv()
15
+ print('Done.')
16
+ ws.close()
17
+
18
+ write_result = write_output_file(output_path, scan_results)
19
+ return write_result
@@ -1,21 +1,26 @@
1
1
  import os
2
2
  from result import Result, Ok, Err
3
3
  from dfpyre import DFTemplate
4
- from pyrecli.util import parse_templates_from_file
4
+ from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
5
5
 
6
6
 
7
- def write_to_directory(dir_name: str, templates: list[DFTemplate], flags: dict[str, int|bool]):
7
+ def write_to_directory(dir_name: str, templates: list[DFTemplate], flags: dict[str, int|bool]) -> Result[None, str]:
8
8
  if not os.path.isdir(dir_name):
9
9
  os.mkdir(dir_name)
10
10
 
11
11
  for template in templates:
12
12
  script_path = f'{dir_name}/{template._get_template_name()}.py'
13
13
  script_string = template.generate_script(**flags)
14
- with open(script_path, 'w') as f:
15
- f.write(script_string)
14
+ try:
15
+ with open(script_path, 'w') as f:
16
+ f.write(script_string)
17
+ except OSError as e:
18
+ return Err(str(e))
19
+
20
+ return Ok(None)
16
21
 
17
22
 
18
- def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dict[str, int|bool]):
23
+ def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dict[str, int|bool]) -> Result[None, str]:
19
24
  file_content = []
20
25
  for i, template in enumerate(templates):
21
26
  if i == 0:
@@ -23,20 +28,21 @@ def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dic
23
28
  else:
24
29
  template_script = template.generate_script(include_import=False, assign_variable=True, **flags)
25
30
  file_content.append(template_script)
26
-
27
- with open(file_path, 'w') as f:
28
- f.write('\n\n'.join(file_content))
31
+
32
+ return write_output_file(file_path, '\n\n'.join(file_content))
29
33
 
30
34
 
31
35
  def script_command(input_path: str, output_path: str, one_file: bool, flags: dict[str, int|bool]) -> Result[None, str]:
32
- templates_result = parse_templates_from_file(input_path)
36
+ input_result = read_input_file(input_path)
37
+ if input_result.is_err():
38
+ return Err(input_result.err_value)
39
+
40
+ templates_result = parse_templates_from_string(input_result.ok_value)
33
41
  if templates_result.is_err():
34
42
  return Err(templates_result.err_value)
35
43
  templates = templates_result.ok_value
36
44
 
37
- if one_file:
38
- write_to_single_file(output_path, templates, flags)
39
- else:
40
- write_to_directory(output_path, templates, flags)
41
-
42
- return Ok(None)
45
+ if one_file or output_path == '-':
46
+ return write_to_single_file(output_path, templates, flags)
47
+
48
+ return write_to_directory(output_path, templates, flags)
@@ -1,9 +1,13 @@
1
1
  from result import Result, Ok, Err
2
- from pyrecli.util import connect_to_codeclient, parse_templates_from_file
2
+ from pyrecli.util import connect_to_codeclient, read_input_file, parse_templates_from_string
3
3
 
4
4
 
5
5
  def send_command(input_path: str) -> Result[None, str]:
6
- templates_result = parse_templates_from_file(input_path)
6
+ input_result = read_input_file(input_path)
7
+ if input_result.is_err():
8
+ return Err(input_result.err_value)
9
+
10
+ templates_result = parse_templates_from_string(input_result.ok_value)
7
11
  if templates_result.is_err():
8
12
  return Err(templates_result.err_value)
9
13
  templates = templates_result.ok_value
@@ -0,0 +1,21 @@
1
+ from result import Result, Err
2
+ from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
3
+
4
+ def slice_command(input_path: str, output_path: str, target_length: int) -> Result[None, str]:
5
+ input_result = read_input_file(input_path)
6
+ if input_result.is_err():
7
+ return Err(input_result.err_value)
8
+
9
+ templates_result = parse_templates_from_string(input_result.ok_value)
10
+ if templates_result.is_err():
11
+ return Err(templates_result.err_value)
12
+ templates = templates_result.ok_value
13
+
14
+ if not templates:
15
+ return Err(f'Could not find any templates in {input_path}')
16
+
17
+ first_template = templates[0]
18
+ sliced_templates = first_template.slice(target_length)
19
+ built_templates = [t.build() for t in sliced_templates]
20
+
21
+ return write_output_file(output_path, '\n'.join(built_templates))
@@ -8,6 +8,15 @@ from pyrecli.command.script import script_command
8
8
  from pyrecli.command.rename import rename_command
9
9
  from pyrecli.command.grabinv import grabinv_command
10
10
  from pyrecli.command.docs import docs_command
11
+ from pyrecli.command.slice import slice_command
12
+
13
+
14
+ def slice_target_length(value):
15
+ MINIMUM_LENGTH = 5
16
+ ivalue = int(value)
17
+ if ivalue < MINIMUM_LENGTH:
18
+ raise argparse.ArgumentTypeError(f'Target length must be at least {MINIMUM_LENGTH} codeblocks')
19
+ return ivalue
11
20
 
12
21
 
13
22
  def main():
@@ -36,18 +45,23 @@ def main():
36
45
  parser_rename.add_argument('var_to_rename', help='The variable to rename', type=str)
37
46
  parser_rename.add_argument('new_var_name', help='The new name for the variable', type=str)
38
47
  parser_rename.add_argument('--var_to_rename_scope', '-s', help='The scope to match', type=str, default=None)
39
- parser_rename.add_argument('--output_path', '-o', help='The file or directory to output to', type=str, default=None)
48
+ parser_rename.add_argument('--output_path', '-o', help='The file to output to', type=str, default=None)
40
49
 
41
50
  parser_grabinv = subparsers.add_parser('grabinv', help='Save all templates in the inventory to a file with CodeClient')
42
51
  parser_grabinv.add_argument('output_path', help='The file to output template data to', type=str)
43
52
 
44
53
  parser_docs = subparsers.add_parser('docs', help='Generate markdown documentation from template data')
45
54
  parser_docs.add_argument('input_path', help='The file containing template data', type=str)
46
- parser_docs.add_argument('output_path', help='The file or directory to output to', type=str)
55
+ parser_docs.add_argument('output_path', help='The file to output to', type=str)
47
56
  parser_docs.add_argument('title', help='The title for the docs', type=str)
48
57
  parser_docs.add_argument('--include_hidden', '-ih', help='Include hidden functions and processes', action='store_true')
49
58
  parser_docs.add_argument('--notoc', help='Omit the table of contents', action='store_true')
50
59
 
60
+ parser_slice = subparsers.add_parser('slice', help='Slice a template into multiple smaller templates')
61
+ parser_slice.add_argument('input_path', help='The file containing template data', type=str)
62
+ parser_slice.add_argument('output_path', help='The file to output template data to', type=str)
63
+ parser_slice.add_argument('target_length', help='The maximum length of each sliced template', type=slice_target_length)
64
+
51
65
  parsed_args = parser.parse_args()
52
66
 
53
67
  match parsed_args.command:
@@ -81,6 +95,12 @@ def main():
81
95
  parsed_args.input_path, parsed_args.output_path,
82
96
  parsed_args.title, parsed_args.include_hidden, parsed_args.notoc
83
97
  )
98
+
99
+ case 'slice':
100
+ command_result = slice_command(
101
+ parsed_args.input_path, parsed_args.output_path,
102
+ parsed_args.target_length
103
+ )
84
104
 
85
105
  if command_result.is_err():
86
106
  print(command_result.err_value)
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import re
3
+ import sys
3
4
  from result import Result, Ok, Err
4
5
  import websocket
5
6
  from dfpyre import DFTemplate
@@ -31,12 +32,8 @@ def connect_to_codeclient(scopes: str|None=None) -> Result[websocket.WebSocket,
31
32
  return Ok(ws)
32
33
 
33
34
 
34
- def parse_templates_from_file(path: str) -> Result[list[DFTemplate], str]:
35
- if not os.path.isfile(path):
36
- return Err(f'"{path}" is not a file.')
37
-
38
- with open(path, 'r') as f:
39
- template_codes = f.read().split('\n')
35
+ def parse_templates_from_string(templates: str) -> Result[list[DFTemplate], str]:
36
+ template_codes = templates.split('\n')
40
37
 
41
38
  for i, template_code in enumerate(template_codes):
42
39
  if not BASE64_REGEX.match(template_code):
@@ -46,3 +43,34 @@ def parse_templates_from_file(path: str) -> Result[list[DFTemplate], str]:
46
43
  return Ok([DFTemplate.from_code(c) for c in template_codes])
47
44
  except Exception as e:
48
45
  return Err(str(e))
46
+
47
+
48
+ def read_input_file(path: str) -> Result[str, str]:
49
+ if path == '-':
50
+ try:
51
+ input_string = sys.stdin.read()
52
+ except EOFError:
53
+ pass
54
+ return Ok(input_string.strip())
55
+
56
+ if not os.path.isfile(path):
57
+ return Err(f'"{path}" is not a file.')
58
+
59
+ try:
60
+ with open(path, 'r') as f:
61
+ return Ok(f.read())
62
+ except OSError as e:
63
+ return Err(str(e))
64
+
65
+
66
+ def write_output_file(path: str, content: str) -> Result[None, str]:
67
+ if path == '-':
68
+ print(content, end='')
69
+ else:
70
+ try:
71
+ with open(path, 'w') as f:
72
+ f.write(content)
73
+ except OSError as e:
74
+ return Err(str(e))
75
+
76
+ return Ok(None)
@@ -1,24 +0,0 @@
1
- from result import Result, Ok, Err
2
- from pyrecli.util import connect_to_codeclient
3
-
4
-
5
- def scan_command(output_path: str) -> Result[None, str]:
6
- ws_result = connect_to_codeclient('read_plot')
7
- if ws_result.is_err():
8
- return Err(ws_result.err_value)
9
- ws = ws_result.ok_value
10
-
11
- print('Scanning plot...')
12
- ws.send('scan')
13
-
14
- scan_results = ws.recv()
15
- print('Done.')
16
- ws.close()
17
-
18
- with open(output_path, 'w') as f:
19
- f.write(scan_results)
20
-
21
- amount_templates = scan_results.count('\n')
22
- print(f'Scanned {amount_templates} template{"s" if amount_templates != 1 else ''} successfully.')
23
-
24
- return Ok(None)
File without changes
File without changes
File without changes