snail-lang 0.7.7__tar.gz → 0.7.8__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.
Files changed (59) hide show
  1. {snail_lang-0.7.7 → snail_lang-0.7.8}/Cargo.lock +5 -5
  2. {snail_lang-0.7.7 → snail_lang-0.7.8}/PKG-INFO +6 -2
  3. {snail_lang-0.7.7 → snail_lang-0.7.8}/README.md +1 -1
  4. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-ast/Cargo.toml +1 -1
  5. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-error/Cargo.toml +1 -1
  6. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/Cargo.toml +1 -1
  7. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/lib.rs +8 -2
  8. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/snail.pest +1 -1
  9. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/Cargo.toml +1 -1
  10. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/awk.rs +120 -68
  11. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/constants.rs +0 -2
  12. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/program.rs +9 -0
  13. {snail_lang-0.7.7 → snail_lang-0.7.8}/pyproject.toml +29 -1
  14. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/cli.py +15 -5
  15. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/__init__.py +5 -1
  16. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/structured_accessor.py +1 -1
  17. {snail_lang-0.7.7 → snail_lang-0.7.8}/Cargo.toml +0 -0
  18. {snail_lang-0.7.7 → snail_lang-0.7.8}/LICENSE +0 -0
  19. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-ast/README.md +0 -0
  20. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-ast/src/ast.rs +0 -0
  21. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-ast/src/awk.rs +0 -0
  22. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-ast/src/lib.rs +0 -0
  23. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-error/README.md +0 -0
  24. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-error/src/lib.rs +0 -0
  25. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/README.md +0 -0
  26. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/awk.rs +0 -0
  27. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/expr.rs +0 -0
  28. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/literal.rs +0 -0
  29. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/stmt.rs +0 -0
  30. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/string.rs +0 -0
  31. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/src/util.rs +0 -0
  32. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/common.rs +0 -0
  33. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/errors.rs +0 -0
  34. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/parser.rs +0 -0
  35. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/statements.rs +0 -0
  36. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/syntax_expressions.rs +0 -0
  37. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-parser/tests/syntax_strings.rs +0 -0
  38. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/build.rs +0 -0
  39. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/compiler.rs +0 -0
  40. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lib.rs +0 -0
  41. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/linecache.rs +0 -0
  42. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/desugar.rs +0 -0
  43. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/expr.rs +0 -0
  44. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/helpers.rs +0 -0
  45. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/map.rs +0 -0
  46. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/mod.rs +0 -0
  47. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/operators.rs +0 -0
  48. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/py_ast.rs +0 -0
  49. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/stmt.rs +0 -0
  50. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/lower/validate.rs +0 -0
  51. {snail_lang-0.7.7 → snail_lang-0.7.8}/crates/snail-python/src/profiling.rs +0 -0
  52. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/__init__.py +0 -0
  53. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/augmented.py +0 -0
  54. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/compact_try.py +0 -0
  55. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/env.py +0 -0
  56. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/lazy_file.py +0 -0
  57. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/lazy_text.py +0 -0
  58. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/regex.py +0 -0
  59. {snail_lang-0.7.7 → snail_lang-0.7.8}/python/snail/runtime/subprocess.py +0 -0
@@ -312,25 +312,25 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
312
312
 
313
313
  [[package]]
314
314
  name = "snail-ast"
315
- version = "0.7.7"
315
+ version = "0.7.8"
316
316
 
317
317
  [[package]]
318
318
  name = "snail-error"
319
- version = "0.7.7"
319
+ version = "0.7.8"
320
320
  dependencies = [
321
321
  "snail-ast",
322
322
  ]
323
323
 
324
324
  [[package]]
325
325
  name = "snail-lower"
326
- version = "0.7.7"
326
+ version = "0.7.8"
327
327
  dependencies = [
328
328
  "snail-python",
329
329
  ]
330
330
 
331
331
  [[package]]
332
332
  name = "snail-parser"
333
- version = "0.7.7"
333
+ version = "0.7.8"
334
334
  dependencies = [
335
335
  "pest",
336
336
  "pest_derive",
@@ -340,7 +340,7 @@ dependencies = [
340
340
 
341
341
  [[package]]
342
342
  name = "snail-python"
343
- version = "0.7.7"
343
+ version = "0.7.8"
344
344
  dependencies = [
345
345
  "pyo3",
346
346
  "snail-ast",
@@ -1,10 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snail-lang
3
- Version: 0.7.7
3
+ Version: 0.7.8
4
4
  Requires-Dist: astunparse>=1.6.3 ; python_full_version < '3.9'
5
5
  Requires-Dist: jmespath>=1.0.1
6
6
  Requires-Dist: maturin>=1.5 ; extra == 'dev'
7
+ Requires-Dist: black ; extra == 'dev'
8
+ Requires-Dist: isort ; extra == 'dev'
9
+ Requires-Dist: mypy ; extra == 'dev'
7
10
  Requires-Dist: pytest ; extra == 'dev'
11
+ Requires-Dist: ruff ; extra == 'dev'
8
12
  Provides-Extra: dev
9
13
  License-File: LICENSE
10
14
  Summary: Snail programming language interpreter
@@ -73,7 +77,7 @@ END { print("done") }
73
77
  | `$f` | All fields as a list |
74
78
  | `$n` | Global line number (across all files) |
75
79
  | `$fn` | Per-file line number |
76
- | `$p` | Current file path |
80
+ | `$src` | Current file path |
77
81
  | `$m` | Last regex match object |
78
82
 
79
83
 
@@ -60,7 +60,7 @@ END { print("done") }
60
60
  | `$f` | All fields as a list |
61
61
  | `$n` | Global line number (across all files) |
62
62
  | `$fn` | Per-file line number |
63
- | `$p` | Current file path |
63
+ | `$src` | Current file path |
64
64
  | `$m` | Last regex match object |
65
65
 
66
66
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-ast"
3
- version = "0.7.7"
3
+ version = "0.7.8"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-error"
3
- version = "0.7.7"
3
+ version = "0.7.8"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-parser"
3
- version = "0.7.7"
3
+ version = "0.7.8"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -159,11 +159,14 @@ pub fn parse_awk_program_with_begin_end(
159
159
  Ok(program)
160
160
  }
161
161
 
162
- const AWK_ONLY_NAMES: [&str; 5] = ["$n", "$fn", "$p", "$m", "$f"];
162
+ const AWK_ONLY_NAMES: [&str; 4] = ["$n", "$fn", "$m", "$f"];
163
163
  const AWK_ONLY_MESSAGE: &str = "awk variables are only valid in awk mode; use --awk";
164
164
 
165
- const MAP_ONLY_NAMES: [&str; 3] = ["$src", "$fd", "$text"];
165
+ const MAP_ONLY_NAMES: [&str; 2] = ["$fd", "$text"];
166
166
  const MAP_ONLY_MESSAGE: &str = "map variables are only valid in map mode; use --map";
167
+ const MAP_OR_AWK_NAMES: [&str; 1] = ["$src"];
168
+ const MAP_OR_AWK_MESSAGE: &str =
169
+ "map/awk variables are only valid in map or awk mode; use --map or --awk";
167
170
 
168
171
  /// Parses a map program that processes files one at a time.
169
172
  /// Allows map variables ($src, $fd, $text) but rejects awk variables.
@@ -933,6 +936,9 @@ fn validate_expr(expr: &Expr, source: &str) -> Result<(), ParseError> {
933
936
  if MAP_ONLY_NAMES.contains(&name.as_str()) {
934
937
  return Err(error_with_span(MAP_ONLY_MESSAGE, span.clone(), source));
935
938
  }
939
+ if MAP_OR_AWK_NAMES.contains(&name.as_str()) {
940
+ return Err(error_with_span(MAP_OR_AWK_MESSAGE, span.clone(), source));
941
+ }
936
942
  }
937
943
  Expr::Placeholder { .. } => {}
938
944
  Expr::FieldIndex { span, .. } => {
@@ -259,7 +259,7 @@ none = { "None" }
259
259
  // Special variables: exception, AWK fields, map vars, injected vars
260
260
  exception_var = { "$e" ~ !ident_continue }
261
261
  field_index_var = @{ "$" ~ ASCII_DIGIT+ }
262
- injected_var = { "$text" | "$src" | "$env" | "$fn" | "$fd" | "$n" | "$p" | "$m" | "$f" }
262
+ injected_var = { "$text" | "$src" | "$env" | "$fn" | "$fd" | "$n" | "$m" | "$f" }
263
263
 
264
264
  // Number, string, and regex literals
265
265
  number = @{ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-python"
3
- version = "0.7.7"
3
+ version = "0.7.8"
4
4
  edition.workspace = true
5
5
  build = "build.rs"
6
6
 
@@ -23,41 +23,27 @@ pub(crate) fn lower_awk_file_loop_with_auto_print(
23
23
  span,
24
24
  )?);
25
25
 
26
- let stdin_body = vec![
27
- assign_name(
28
- builder,
29
- "__snail_file",
30
- builder
31
- .call_node(
32
- "Attribute",
33
- vec![
34
- name_expr(
35
- builder,
36
- "sys",
37
- span,
38
- builder.load_ctx().map_err(py_err_to_lower)?,
39
- )?,
40
- "stdin".to_string().into_py(builder.py()),
26
+ let stdin_assign = assign_name(
27
+ builder,
28
+ "__snail_file",
29
+ builder
30
+ .call_node(
31
+ "Attribute",
32
+ vec![
33
+ name_expr(
34
+ builder,
35
+ "sys",
36
+ span,
41
37
  builder.load_ctx().map_err(py_err_to_lower)?,
42
- ],
43
- span,
44
- )
45
- .map_err(py_err_to_lower)?,
46
- span,
47
- )?,
48
- lower_awk_line_loop_with_auto_print(
49
- builder,
50
- program,
51
- span,
52
- name_expr(
53
- builder,
54
- "__snail_file",
38
+ )?,
39
+ "stdin".to_string().into_py(builder.py()),
40
+ builder.load_ctx().map_err(py_err_to_lower)?,
41
+ ],
55
42
  span,
56
- builder.load_ctx().map_err(py_err_to_lower)?,
57
- )?,
58
- auto_print,
59
- )?,
60
- ];
43
+ )
44
+ .map_err(py_err_to_lower)?,
45
+ span,
46
+ )?;
61
47
 
62
48
  let open_call = builder
63
49
  .call_node(
@@ -85,46 +71,35 @@ pub(crate) fn lower_awk_file_loop_with_auto_print(
85
71
  )
86
72
  .map_err(py_err_to_lower)?;
87
73
 
88
- let with_item = builder
89
- .call_node_no_loc(
90
- "withitem",
91
- vec![
92
- open_call,
93
- name_expr(
94
- builder,
95
- "__snail_file",
96
- span,
97
- builder.store_ctx().map_err(py_err_to_lower)?,
98
- )?,
99
- ],
100
- )
101
- .map_err(py_err_to_lower)?;
102
- let with_stmt = builder
74
+ let enter_context_call = builder
103
75
  .call_node(
104
- "With",
76
+ "Call",
105
77
  vec![
106
- PyList::new_bound(builder.py(), vec![with_item]).into_py(builder.py()),
107
- PyList::new_bound(
108
- builder.py(),
109
- vec![lower_awk_line_loop_with_auto_print(
110
- builder,
111
- program,
112
- span,
113
- name_expr(
114
- builder,
115
- "__snail_file",
116
- span,
78
+ builder
79
+ .call_node(
80
+ "Attribute",
81
+ vec![
82
+ name_expr(
83
+ builder,
84
+ "__snail_stack",
85
+ span,
86
+ builder.load_ctx().map_err(py_err_to_lower)?,
87
+ )?,
88
+ "enter_context".to_string().into_py(builder.py()),
117
89
  builder.load_ctx().map_err(py_err_to_lower)?,
118
- )?,
119
- auto_print,
120
- )?],
121
- )
122
- .into_py(builder.py()),
90
+ ],
91
+ span,
92
+ )
93
+ .map_err(py_err_to_lower)?,
94
+ PyList::new_bound(builder.py(), vec![open_call]).into_py(builder.py()),
95
+ PyList::empty_bound(builder.py()).into_py(builder.py()),
123
96
  ],
124
97
  span,
125
98
  )
126
99
  .map_err(py_err_to_lower)?;
127
100
 
101
+ let file_assign = assign_name(builder, "__snail_file", enter_context_call, span)?;
102
+
128
103
  let test = builder
129
104
  .call_node(
130
105
  "Compare",
@@ -161,14 +136,80 @@ pub(crate) fn lower_awk_file_loop_with_auto_print(
161
136
  "If",
162
137
  vec![
163
138
  test,
164
- PyList::new_bound(builder.py(), stdin_body).into_py(builder.py()),
165
- PyList::new_bound(builder.py(), vec![with_stmt]).into_py(builder.py()),
139
+ PyList::new_bound(builder.py(), vec![stdin_assign]).into_py(builder.py()),
140
+ PyList::new_bound(builder.py(), vec![file_assign]).into_py(builder.py()),
166
141
  ],
167
142
  span,
168
143
  )
169
144
  .map_err(py_err_to_lower)?;
170
145
 
171
- file_loop.push(if_stmt);
146
+ let exit_stack_call = builder
147
+ .call_node(
148
+ "Call",
149
+ vec![
150
+ builder
151
+ .call_node(
152
+ "Attribute",
153
+ vec![
154
+ name_expr(
155
+ builder,
156
+ "contextlib",
157
+ span,
158
+ builder.load_ctx().map_err(py_err_to_lower)?,
159
+ )?,
160
+ "ExitStack".to_string().into_py(builder.py()),
161
+ builder.load_ctx().map_err(py_err_to_lower)?,
162
+ ],
163
+ span,
164
+ )
165
+ .map_err(py_err_to_lower)?,
166
+ PyList::empty_bound(builder.py()).into_py(builder.py()),
167
+ PyList::empty_bound(builder.py()).into_py(builder.py()),
168
+ ],
169
+ span,
170
+ )
171
+ .map_err(py_err_to_lower)?;
172
+
173
+ let with_item = builder
174
+ .call_node_no_loc(
175
+ "withitem",
176
+ vec![
177
+ exit_stack_call,
178
+ name_expr(
179
+ builder,
180
+ "__snail_stack",
181
+ span,
182
+ builder.store_ctx().map_err(py_err_to_lower)?,
183
+ )?,
184
+ ],
185
+ )
186
+ .map_err(py_err_to_lower)?;
187
+
188
+ let line_loop = lower_awk_line_loop_with_auto_print(
189
+ builder,
190
+ program,
191
+ span,
192
+ name_expr(
193
+ builder,
194
+ "__snail_file",
195
+ span,
196
+ builder.load_ctx().map_err(py_err_to_lower)?,
197
+ )?,
198
+ auto_print,
199
+ )?;
200
+
201
+ let with_stmt = builder
202
+ .call_node(
203
+ "With",
204
+ vec![
205
+ PyList::new_bound(builder.py(), vec![with_item]).into_py(builder.py()),
206
+ PyList::new_bound(builder.py(), vec![if_stmt, line_loop]).into_py(builder.py()),
207
+ ],
208
+ span,
209
+ )
210
+ .map_err(py_err_to_lower)?;
211
+
212
+ file_loop.push(with_stmt);
172
213
  Ok(file_loop)
173
214
  }
174
215
 
@@ -344,6 +385,17 @@ pub(crate) fn lower_awk_line_loop_with_auto_print(
344
385
  )?,
345
386
  span,
346
387
  )?);
388
+ loop_body.push(assign_name(
389
+ builder,
390
+ SNAIL_MAP_SRC_PYVAR,
391
+ name_expr(
392
+ builder,
393
+ "__snail_path",
394
+ span,
395
+ builder.load_ctx().map_err(py_err_to_lower)?,
396
+ )?,
397
+ span,
398
+ )?);
347
399
 
348
400
  loop_body.extend(lower_awk_rules_with_auto_print(
349
401
  builder,
@@ -24,7 +24,6 @@ pub(crate) const SNAIL_INCR_TMP: &str = "__snail_incr_tmp";
24
24
  // Awk-related constants (public within crate)
25
25
  pub(crate) const SNAIL_AWK_NR: &str = "$n";
26
26
  pub(crate) const SNAIL_AWK_FNR: &str = "$fn";
27
- pub(crate) const SNAIL_AWK_PATH: &str = "$p";
28
27
  pub(crate) const SNAIL_AWK_MATCH: &str = "$m";
29
28
  pub(crate) const SNAIL_AWK_FIELDS: &str = "$f";
30
29
  pub(crate) const SNAIL_AWK_LINE_PYVAR: &str = "__snail_line";
@@ -53,7 +52,6 @@ pub(crate) fn injected_py_name(name: &str) -> Option<&'static str> {
53
52
  // Awk variables
54
53
  SNAIL_AWK_NR => Some(SNAIL_AWK_NR_PYVAR),
55
54
  SNAIL_AWK_FNR => Some(SNAIL_AWK_FNR_PYVAR),
56
- SNAIL_AWK_PATH => Some(SNAIL_AWK_PATH_PYVAR),
57
55
  SNAIL_AWK_MATCH => Some(SNAIL_AWK_MATCH_PYVAR),
58
56
  SNAIL_AWK_FIELDS => Some(SNAIL_AWK_FIELDS_PYVAR),
59
57
  // Map variables
@@ -98,6 +98,15 @@ pub fn lower_awk_program_with_auto_print(
98
98
  ],
99
99
  )
100
100
  .map_err(py_err_to_lower)?,
101
+ builder
102
+ .call_node_no_loc(
103
+ "alias",
104
+ vec![
105
+ "contextlib".to_string().into_py(builder.py()),
106
+ builder.py().None().into_py(builder.py()),
107
+ ],
108
+ )
109
+ .map_err(py_err_to_lower)?,
101
110
  ],
102
111
  )
103
112
  .into_py(builder.py()),
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "snail-lang"
7
- version = "0.7.7"
7
+ version = "0.7.8"
8
8
  description = "Snail programming language interpreter"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -17,7 +17,11 @@ dependencies = [
17
17
  [project.optional-dependencies]
18
18
  dev = [
19
19
  "maturin>=1.5",
20
+ "black",
21
+ "isort",
22
+ "mypy",
20
23
  "pytest",
24
+ "ruff",
21
25
  ]
22
26
 
23
27
  [project.scripts]
@@ -28,3 +32,27 @@ bindings = "pyo3"
28
32
  python-source = "python"
29
33
  module-name = "snail._native"
30
34
  manifest-path = "crates/snail-python/Cargo.toml"
35
+
36
+ [tool.black]
37
+ line-length = 88
38
+ target-version = ["py38"]
39
+
40
+ [tool.isort]
41
+ profile = "black"
42
+ line_length = 88
43
+ src_paths = ["python"]
44
+
45
+ [tool.ruff]
46
+ line-length = 88
47
+ target-version = "py38"
48
+ src = ["python"]
49
+ exclude = ["target", ".venv", "build", "dist"]
50
+
51
+ [tool.mypy]
52
+ python_version = "3.12"
53
+ show_error_codes = true
54
+ warn_unused_ignores = true
55
+
56
+ [[tool.mypy.overrides]]
57
+ module = ["snail._native"]
58
+ ignore_missing_imports = true
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import sys
5
- from typing import Optional
5
+ from types import TracebackType
6
+ from typing import Any, Iterable, Optional, cast
6
7
 
7
8
  from . import __build_info__, compile_ast, exec
8
9
 
@@ -58,7 +59,7 @@ def _install_trimmed_excepthook() -> None:
58
59
  def _snail_excepthook(
59
60
  exc_type: type[BaseException],
60
61
  exc: BaseException,
61
- tb: object,
62
+ tb: TracebackType | None,
62
63
  ) -> None:
63
64
  if exc_type is KeyboardInterrupt:
64
65
  return original_excepthook(exc_type, exc, tb)
@@ -72,13 +73,13 @@ def _install_trimmed_excepthook() -> None:
72
73
  )
73
74
  _trim_traceback_exception(tb_exc, internal_files)
74
75
  try:
75
- import _colorize
76
+ import _colorize # type: ignore[import-not-found]
76
77
 
77
78
  colorize = _colorize.can_colorize(file=sys.stderr)
78
79
  except Exception:
79
80
  colorize = hasattr(sys.stderr, "isatty") and sys.stderr.isatty()
80
81
  try:
81
- formatted = tb_exc.format(colorize=colorize)
82
+ formatted = cast(Iterable[str], cast(Any, tb_exc).format(colorize=colorize))
82
83
  except TypeError:
83
84
  formatted = tb_exc.format()
84
85
  for line in formatted:
@@ -407,12 +408,21 @@ def main(argv: Optional[list[str]] = None) -> int:
407
408
  try:
408
409
  output = ast.unparse(python_ast)
409
410
  except AttributeError:
410
- import astunparse
411
+ import astunparse # type: ignore[import-not-found]
411
412
 
412
413
  output = astunparse.unparse(python_ast).rstrip("\n")
413
414
  print(output)
414
415
  return 0
415
416
 
417
+ if mode == "awk" and not args[1:]:
418
+ try:
419
+ is_tty = sys.stdin.isatty()
420
+ except Exception:
421
+ is_tty = False
422
+ if is_tty:
423
+ print('Missing input (see "snail --help")', file=sys.stderr)
424
+ return 1
425
+
416
426
  separators = "".join(namespace.field_separators)
417
427
  field_separators = separators if separators else None
418
428
  include_whitespace = namespace.include_whitespace or field_separators is None
@@ -223,7 +223,9 @@ def _lazy_aug_index(obj, index, value, op: str):
223
223
  return _get_aug_index()(obj, index, value, op)
224
224
 
225
225
 
226
- def __snail_awk_split_internal(line: str, separators: Optional[str], include_whitespace: bool):
226
+ def __snail_awk_split_internal(
227
+ line: str, separators: Optional[str], include_whitespace: bool
228
+ ):
227
229
  if not separators:
228
230
  return line.split()
229
231
  if not include_whitespace:
@@ -244,10 +246,12 @@ def __snail_awk_split_internal(line: str, separators: Optional[str], include_whi
244
246
  _awk_split_cache[(separators, True)] = regex
245
247
  return regex.split(stripped)
246
248
 
249
+
247
250
  def __snail_awk_split(line: str, separators: Optional[str], include_whitespace: bool):
248
251
  fields = __snail_awk_split_internal(line, separators, include_whitespace)
249
252
  return [f for f in fields if f]
250
253
 
254
+
251
255
  def __snail_partial(func, /, *args, **kwargs):
252
256
  import functools
253
257
 
@@ -100,7 +100,7 @@ def __snail_jmespath_query(query: str):
100
100
  Used by the $[query] syntax which lowers to __snail_jmespath_query(query).
101
101
  """
102
102
 
103
- import jmespath as _jmespath
103
+ import jmespath as _jmespath # type: ignore[import-untyped]
104
104
 
105
105
  transpiled = _transpile_jmespath_query(query)
106
106
 
File without changes
File without changes