suma 0.5.10 → 0.5.11
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.
- checksums.yaml +4 -4
- data/lib/suma/cli/build.rb +16 -3
- data/lib/suma/closure_validator.rb +20 -20
- data/lib/suma/corpus_graph.rb +1 -1
- data/lib/suma/owl_projection.rb +3 -3
- data/lib/suma/processor.rb +6 -0
- data/lib/suma/schema_exporter.rb +12 -0
- data/lib/suma/schema_index.rb +5 -5
- data/lib/suma/staged_collection_builder.rb +137 -9
- data/lib/suma/term_extractor.rb +10 -4
- data/lib/suma/urn_resolver.rb +3 -3
- data/lib/suma/version.rb +1 -1
- data/lib/suma/worker_pool.rb +5 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7f936716b0ec73649dcebc11349ff94e9979960e0481d7ea48d7d2cfa5edbcfd
|
|
4
|
+
data.tar.gz: 206887bffa8fb3081bc17a402fccf7bcab927671ffe35c3366ce6b269ab50a15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af7f5da53bd4cdbf0195381a166334ed51ef5f58434dd3d13cb7c183f0dfe880ce707d3e0d0f75c0a85ab9748cc1b212784f90d30de317997c932fe6811ad985
|
|
7
|
+
data.tar.gz: d940f6ff6ff164cb4b5a53b1f7c5e45462494d83e3bbf1815b8821e3bb015f5fcb9ec32a8094588dee091cd86b3d0aad4629515e114f5d1399957de22b4bc4df
|
data/lib/suma/cli/build.rb
CHANGED
|
@@ -14,8 +14,15 @@ module Suma
|
|
|
14
14
|
desc: "Generate file that contains all schemas in the collection."
|
|
15
15
|
option :staged, type: :boolean, default: false,
|
|
16
16
|
desc: "Memory-bounded staged build: compile each member in " \
|
|
17
|
-
"its own process (sequential
|
|
18
|
-
"
|
|
17
|
+
"its own process (sequential unless --staged-workers " \
|
|
18
|
+
"is given) and reinflate. For large collections that " \
|
|
19
|
+
"OOM a single-process build (suma#94)."
|
|
20
|
+
option :staged_workers, type: :numeric, default: 1,
|
|
21
|
+
desc: "Concurrent staged render processes (batched " \
|
|
22
|
+
"members per process; requires --staged)"
|
|
23
|
+
option :staged_batch_size, type: :numeric,
|
|
24
|
+
desc: "Members per staged render process " \
|
|
25
|
+
"(default: 25)"
|
|
19
26
|
option :incremental, type: :boolean, default: false,
|
|
20
27
|
desc: "Re-render only changed schemas and their " \
|
|
21
28
|
"transitive dependents (state file defaults " \
|
|
@@ -58,7 +65,6 @@ module Suma
|
|
|
58
65
|
schemas_all_path: schemas_all_path,
|
|
59
66
|
compile: options[:compile],
|
|
60
67
|
output_directory: "_site",
|
|
61
|
-
staged: options[:staged],
|
|
62
68
|
incremental: options[:incremental],
|
|
63
69
|
state_file: options[:state_file],
|
|
64
70
|
jobs: options[:jobs],
|
|
@@ -66,9 +72,16 @@ module Suma
|
|
|
66
72
|
sign_method: options[:sign],
|
|
67
73
|
sign_key: options[:sign_key],
|
|
68
74
|
no_valid: options[:no_valid],
|
|
75
|
+
**staged_options(options),
|
|
69
76
|
).run
|
|
70
77
|
end
|
|
71
78
|
|
|
79
|
+
def staged_options(options)
|
|
80
|
+
{ staged: options[:staged],
|
|
81
|
+
staged_workers: options[:staged_workers],
|
|
82
|
+
staged_batch_size: options[:staged_batch_size] }
|
|
83
|
+
end
|
|
84
|
+
|
|
72
85
|
def log_error(error)
|
|
73
86
|
Utils.log "[ERROR] Error occurred during processing. See details below."
|
|
74
87
|
Utils.log error
|
|
@@ -36,8 +36,8 @@ module Suma
|
|
|
36
36
|
BUILTIN_IDENTIFIERS = %w[
|
|
37
37
|
abs acos asin atan atan2 bexists blength const_e cos exists exp
|
|
38
38
|
format hibound hiindex length lobound loindex log log2 log10 nvl odd
|
|
39
|
-
roles_of sin sizeof sqrt tan typeof usedin value value_in
|
|
40
|
-
value_range value_list_by unique
|
|
39
|
+
roles_of rolesof sin sizeof sqrt tan typeof usedin value value_in
|
|
40
|
+
value_range value_list_by value_unique unique
|
|
41
41
|
self and or xor not in like equal instanceof
|
|
42
42
|
e pi
|
|
43
43
|
].freeze
|
|
@@ -61,9 +61,9 @@ module Suma
|
|
|
61
61
|
parsed = manifest.schemas.map do |entry|
|
|
62
62
|
[entry, Expressir::Express::Parser.from_file(entry.path.to_s)]
|
|
63
63
|
end
|
|
64
|
-
corpus_entities = parsed.flat_map { |_, repo| repo.schemas }
|
|
64
|
+
corpus_entities = parsed.flat_map { |_, repo| Array(repo.schemas) }
|
|
65
65
|
.flat_map do |schema|
|
|
66
|
-
schema.children.grep(Expressir::Model::Declarations::Entity)
|
|
66
|
+
Array(schema.children).grep(Expressir::Model::Declarations::Entity)
|
|
67
67
|
end
|
|
68
68
|
.to_h { |entity| [entity.id.downcase, entity] }
|
|
69
69
|
|
|
@@ -84,10 +84,10 @@ module Suma
|
|
|
84
84
|
def validate_schema(file, schema, corpus_entities = {})
|
|
85
85
|
violations = []
|
|
86
86
|
scope = SchemaScope.new(schema, graph)
|
|
87
|
-
entities = schema.children.grep(Expressir::Model::Declarations::Entity)
|
|
87
|
+
entities = Array(schema.children).grep(Expressir::Model::Declarations::Entity)
|
|
88
88
|
.to_h { |entity| [entity.id.downcase, entity] }
|
|
89
89
|
|
|
90
|
-
schema.interfaces.each do |interface|
|
|
90
|
+
Array(schema.interfaces).each do |interface|
|
|
91
91
|
next if graph.schema_exists?(interface.schema.id)
|
|
92
92
|
|
|
93
93
|
violations << unresolved(file, schema.id, schema.id, "interface",
|
|
@@ -95,7 +95,7 @@ module Suma
|
|
|
95
95
|
"interface target not declared")
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
schema.children.each do |owner|
|
|
98
|
+
Array(schema.children).each do |owner|
|
|
99
99
|
violations.concat(declared_element_references(file, schema.id, owner,
|
|
100
100
|
scope, entities,
|
|
101
101
|
corpus_entities))
|
|
@@ -145,7 +145,7 @@ module Suma
|
|
|
145
145
|
refs = (entity.where_rules || []).flat_map do |rule|
|
|
146
146
|
expression_references(rule.expression, "where", locals)
|
|
147
147
|
end
|
|
148
|
-
entity.attributes.each do |attribute|
|
|
148
|
+
Array(entity.attributes).each do |attribute|
|
|
149
149
|
refs.concat(datatype_references(attribute.type, "attribute"))
|
|
150
150
|
end
|
|
151
151
|
refs.concat(supertype_references(entity))
|
|
@@ -159,7 +159,7 @@ module Suma
|
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def rule_references(rule)
|
|
162
|
-
refs = rule.applies_to.map { |ref| ["rule_applies_to", ref.id] }
|
|
162
|
+
refs = Array(rule.applies_to).map { |ref| ["rule_applies_to", ref.id] }
|
|
163
163
|
locals = Array(rule.variables).filter_map do |variable|
|
|
164
164
|
variable.id&.downcase
|
|
165
165
|
end
|
|
@@ -383,24 +383,24 @@ module Suma
|
|
|
383
383
|
@graph = graph
|
|
384
384
|
@schema_id = schema.id
|
|
385
385
|
|
|
386
|
-
schema.children.each { |declaration| add(declaration) }
|
|
387
|
-
schema.entities.each do |entity|
|
|
388
|
-
entity.attributes.each { |attribute| add(attribute) }
|
|
386
|
+
Array(schema.children).each { |declaration| add(declaration) }
|
|
387
|
+
Array(schema.entities).each do |entity|
|
|
388
|
+
Array(entity.attributes).each { |attribute| add(attribute) }
|
|
389
389
|
end
|
|
390
|
-
schema.rules.each do |rule|
|
|
391
|
-
rule.variables.each { |variable| add(variable) }
|
|
392
|
-
rule.applies_to.each do |ref|
|
|
393
|
-
entity = schema.entities.find do |candidate|
|
|
390
|
+
Array(schema.rules).each do |rule|
|
|
391
|
+
Array(rule.variables).each { |variable| add(variable) }
|
|
392
|
+
Array(rule.applies_to).each do |ref|
|
|
393
|
+
entity = Array(schema.entities).find do |candidate|
|
|
394
394
|
candidate.id&.downcase == ref.id&.downcase
|
|
395
395
|
end
|
|
396
396
|
next unless entity
|
|
397
397
|
|
|
398
|
-
entity.attributes.each { |attribute| add(attribute) }
|
|
398
|
+
Array(entity.attributes).each { |attribute| add(attribute) }
|
|
399
399
|
end
|
|
400
400
|
end
|
|
401
|
-
schema.constants.each { |constant| add(constant) }
|
|
402
|
-
schema.functions.each do |function|
|
|
403
|
-
(function.parameters
|
|
401
|
+
Array(schema.constants).each { |constant| add(constant) }
|
|
402
|
+
Array(schema.functions).each do |function|
|
|
403
|
+
Array(function.parameters).each { |parameter| add(parameter) }
|
|
404
404
|
end
|
|
405
405
|
end
|
|
406
406
|
|
data/lib/suma/corpus_graph.rb
CHANGED
data/lib/suma/owl_projection.rb
CHANGED
|
@@ -167,7 +167,7 @@ module Suma
|
|
|
167
167
|
# the Expressir model and must not shadow declarations).
|
|
168
168
|
def index_elements
|
|
169
169
|
@schemas.each_with_object({}) do |(schema_key, schema), map|
|
|
170
|
-
schema.children.each do |element|
|
|
170
|
+
Array(schema.children).each do |element|
|
|
171
171
|
next unless class_element?(element)
|
|
172
172
|
|
|
173
173
|
map[[schema_key, normalize(element.id)]] = element
|
|
@@ -231,7 +231,7 @@ module Suma
|
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
def project_schema(graph, schema_key, schema)
|
|
234
|
-
schema.children.each do |element|
|
|
234
|
+
Array(schema.children).each do |element|
|
|
235
235
|
case element
|
|
236
236
|
when Expressir::Model::Declarations::Entity
|
|
237
237
|
project_entity(graph, schema_key, element)
|
|
@@ -482,7 +482,7 @@ module Suma
|
|
|
482
482
|
|
|
483
483
|
def validate_materialization(projected)
|
|
484
484
|
projected.flat_map do |schema_key, schema|
|
|
485
|
-
schema.children.filter_map do |element|
|
|
485
|
+
Array(schema.children).filter_map do |element|
|
|
486
486
|
next unless class_element?(element)
|
|
487
487
|
|
|
488
488
|
iri = element_iri(schema_key, element.id)
|
data/lib/suma/processor.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Suma
|
|
|
13
13
|
|
|
14
14
|
def initialize(metanorma_yaml_path:, schemas_all_path:, compile: true,
|
|
15
15
|
output_directory: "_site", staged: false,
|
|
16
|
+
staged_workers: 1, staged_batch_size: nil,
|
|
16
17
|
incremental: false, state_file: nil, jobs: 1,
|
|
17
18
|
provenance_file: nil, sign_method: nil, sign_key: nil,
|
|
18
19
|
no_valid: false)
|
|
@@ -23,6 +24,8 @@ module Suma
|
|
|
23
24
|
# Opt-in memory-bounded staged build (metanorma/suma#94); the default
|
|
24
25
|
# single-process build below is unchanged when false.
|
|
25
26
|
@staged = staged
|
|
27
|
+
@staged_workers = staged_workers
|
|
28
|
+
@staged_batch_size = staged_batch_size
|
|
26
29
|
@incremental = incremental
|
|
27
30
|
@state_file = state_file
|
|
28
31
|
@jobs = jobs
|
|
@@ -181,6 +184,9 @@ module Suma
|
|
|
181
184
|
output_directory: output_directory,
|
|
182
185
|
coverpage: collection_coverpage(collection_config),
|
|
183
186
|
no_valid: @no_valid,
|
|
187
|
+
workers: @staged_workers,
|
|
188
|
+
batch_size: @staged_batch_size ||
|
|
189
|
+
StagedCollectionBuilder::DEFAULT_BATCH_SIZE,
|
|
184
190
|
).build
|
|
185
191
|
else
|
|
186
192
|
Metanorma::Collection.parse(COLLECTION_OUTPUT_PATH)
|
data/lib/suma/schema_exporter.rb
CHANGED
|
@@ -47,6 +47,18 @@ module Suma
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def export_to_directory(schemas)
|
|
50
|
+
run_pool_export(schemas)
|
|
51
|
+
rescue SystemStackError
|
|
52
|
+
# One pathological schema (a very deep AST) can overflow the
|
|
53
|
+
# worker threads' ~1 MB stack. The main thread has the full OS
|
|
54
|
+
# stack and every completed export is cached, so the retry only
|
|
55
|
+
# pays for the schemas that did not finish.
|
|
56
|
+
Utils.log "[export] worker thread stack exhausted; " \
|
|
57
|
+
"retrying the remaining schemas on the main thread"
|
|
58
|
+
schemas.each { |schema| export_single_schema(schema) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def run_pool_export(schemas)
|
|
50
62
|
WorkerPool.new(size: options[:jobs]).map(schemas) do |schema|
|
|
51
63
|
export_single_schema(schema)
|
|
52
64
|
end
|
data/lib/suma/schema_index.rb
CHANGED
|
@@ -40,11 +40,11 @@ module Suma
|
|
|
40
40
|
|
|
41
41
|
def element_collections(schema)
|
|
42
42
|
[
|
|
43
|
-
schema.entities,
|
|
44
|
-
schema.types,
|
|
45
|
-
schema.constants,
|
|
46
|
-
schema.functions,
|
|
47
|
-
schema.rules,
|
|
43
|
+
Array(schema.entities),
|
|
44
|
+
Array(schema.types),
|
|
45
|
+
Array(schema.constants),
|
|
46
|
+
Array(schema.functions),
|
|
47
|
+
Array(schema.rules),
|
|
48
48
|
schema.procedures,
|
|
49
49
|
schema.subtype_constraints,
|
|
50
50
|
]
|
|
@@ -16,10 +16,12 @@ module Suma
|
|
|
16
16
|
# stub-bearing Semantic XML + anchors to a durable content-addressed store
|
|
17
17
|
# (the metanorma engine's +preserve_unresolved:+ / +artifact_store_dir:+
|
|
18
18
|
# render options, metanorma/metanorma#576);
|
|
19
|
-
# 2. runs those per-member processes SEQUENTIALLY
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
19
|
+
# 2. runs those per-member processes SEQUENTIALLY by default; with
|
|
20
|
+
# +workers+ > 1, batches of members are rendered by that many child
|
|
21
|
+
# processes concurrently (safe: members write disjoint store entries,
|
|
22
|
+
# and sectionsplit happens only in the sequential reinflate pass); each
|
|
23
|
+
# process exit hands its memory back to the OS, so peak memory stays
|
|
24
|
+
# bounded by workers x one member; then
|
|
23
25
|
# 3. REINFLATES: a final pass assembles a manifest of the stored stubs and
|
|
24
26
|
# resolves them into real cross-document links (+reinflate:+), producing
|
|
25
27
|
# the same output an all-present build would.
|
|
@@ -34,12 +36,19 @@ module Suma
|
|
|
34
36
|
class StagedCollectionBuilder
|
|
35
37
|
STORE_DIRNAME = ".metanorma-collection-cache"
|
|
36
38
|
|
|
39
|
+
# Members rendered per child process: one Metanorma boot amortised over
|
|
40
|
+
# a batch of small members keeps boot cost under a few percent.
|
|
41
|
+
DEFAULT_BATCH_SIZE = 25
|
|
42
|
+
|
|
37
43
|
# +collection_config_path+ is the emitted collection manifest (the same
|
|
38
44
|
# +collection-output.yaml+ the normal build renders). Member filerefs are
|
|
39
45
|
# relative to its directory, so all per-member manifests are written there.
|
|
40
|
-
#
|
|
46
|
+
# +workers+ > 1 runs that many renderer processes concurrently over batches
|
|
47
|
+
# of +batch_size+ members; members write disjoint, identifier-keyed store
|
|
48
|
+
# entries, so children cannot race. (a data-declaring initializer)
|
|
41
49
|
def initialize(collection_config_path:, output_directory:, coverpage: nil,
|
|
42
|
-
store_dir: nil, formats: %i[xml html], no_valid: false
|
|
50
|
+
store_dir: nil, formats: %i[xml html], no_valid: false,
|
|
51
|
+
workers: 1, batch_size: DEFAULT_BATCH_SIZE) # rubocop:disable Metrics/ParameterLists
|
|
43
52
|
@config_path = collection_config_path
|
|
44
53
|
@config_dir = File.dirname(File.expand_path(collection_config_path))
|
|
45
54
|
@output_directory = output_directory
|
|
@@ -47,20 +56,139 @@ module Suma
|
|
|
47
56
|
@store_dir = store_dir || File.join(output_directory, STORE_DIRNAME)
|
|
48
57
|
@formats = formats
|
|
49
58
|
@no_valid = no_valid
|
|
59
|
+
@workers = [workers.to_i, 1].max
|
|
60
|
+
@batch_size = [batch_size.to_i, 1].max
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
def build
|
|
53
64
|
FileUtils.mkdir_p(@output_directory)
|
|
54
65
|
FileUtils.mkdir_p(@store_dir)
|
|
55
66
|
members = collection_members
|
|
56
|
-
Utils.log "[staged] #{members.size} members;
|
|
57
|
-
"
|
|
58
|
-
|
|
67
|
+
Utils.log "[staged] #{members.size} members; workers: #{@workers}; " \
|
|
68
|
+
"batch size: #{@batch_size}; store: #{@store_dir}"
|
|
69
|
+
if @workers == 1
|
|
70
|
+
members.each_with_index { |member, i| stage_member(member, i) }
|
|
71
|
+
else
|
|
72
|
+
stage_parallel(members)
|
|
73
|
+
end
|
|
59
74
|
reinflate(members)
|
|
60
75
|
end
|
|
61
76
|
|
|
62
77
|
private
|
|
63
78
|
|
|
79
|
+
# Batch the members and run +@workers+ renderer child processes
|
|
80
|
+
# concurrently. Each batch file carries fully-rendered single-member
|
|
81
|
+
# manifests; children claim batches through O_EXCL markers, so a slow
|
|
82
|
+
# worker never starves. One Metanorma boot amortises over
|
|
83
|
+
# +@batch_size+ members (the boot dominates small members), and each
|
|
84
|
+
# child exit hands its memory back to the OS. Members write disjoint,
|
|
85
|
+
# identifier-keyed store entries, so concurrent children cannot race;
|
|
86
|
+
# sectionsplit only happens at reinflate (a single sequential pass).
|
|
87
|
+
def stage_parallel(members)
|
|
88
|
+
paths, count = write_batch_files(members)
|
|
89
|
+
run_batch_workers(count)
|
|
90
|
+
ensure
|
|
91
|
+
remove_batch_files(paths, count)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def write_batch_files(members)
|
|
95
|
+
batches = batch_manifests(members)
|
|
96
|
+
paths = write_batch_bins(batches)
|
|
97
|
+
Utils.log "[staged] #{batches.size} batches over #{@workers} workers"
|
|
98
|
+
[paths, batches.size]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def batch_manifests(members)
|
|
102
|
+
members.each_slice(@batch_size).map do |slice|
|
|
103
|
+
slice.map do |member|
|
|
104
|
+
base_manifest.merge("manifest" => collection_manifest([member]))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def write_batch_bins(batches)
|
|
110
|
+
batches.map.with_index do |batch, i|
|
|
111
|
+
path = File.join(@config_dir, "._suma_batch_#{i}.bin")
|
|
112
|
+
File.binwrite(path, Marshal.dump(batch))
|
|
113
|
+
path
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def run_batch_workers(count)
|
|
118
|
+
children = Array.new([@workers, count].min) do
|
|
119
|
+
Process.spawn(RbConfig.ruby, "-e", child_script, @config_dir,
|
|
120
|
+
count.to_s, @store_dir, @output_directory,
|
|
121
|
+
@no_valid ? "1" : "0", chdir: @config_dir)
|
|
122
|
+
end
|
|
123
|
+
failed = children
|
|
124
|
+
.filter_map { |pid| Process.waitpid2(pid) }
|
|
125
|
+
.map(&:last).reject(&:success?).size
|
|
126
|
+
return if failed.zero?
|
|
127
|
+
|
|
128
|
+
raise "[staged] #{failed} of #{@workers} batch workers failed; " \
|
|
129
|
+
"see the worker output above"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def remove_batch_files(paths, count)
|
|
133
|
+
paths&.each { |path| FileUtils.rm_f(path) }
|
|
134
|
+
(0...(count || 0)).each do |i|
|
|
135
|
+
FileUtils.rm_f(File.join(@config_dir, "._suma_batch_#{i}.claim"))
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Child renderer: claims and renders batches until none remain. The
|
|
140
|
+
# Metanorma boot is paid once per child, and GC runs between members to
|
|
141
|
+
# bound the heap. Any render failure exits nonzero and fails the build.
|
|
142
|
+
def child_script
|
|
143
|
+
<<~RUBY
|
|
144
|
+
require "yaml"
|
|
145
|
+
require "fileutils"
|
|
146
|
+
require "metanorma"
|
|
147
|
+
config_dir, batch_count, store_dir, out_dir, no_valid = ARGV
|
|
148
|
+
count = Integer(batch_count)
|
|
149
|
+
claimed = 0
|
|
150
|
+
loop do
|
|
151
|
+
idx = (0...count).find do |i|
|
|
152
|
+
begin
|
|
153
|
+
File.open(File.join(config_dir, "._suma_batch_\#{i}.claim"),
|
|
154
|
+
File::RDWR | File::CREAT | File::EXCL) {}
|
|
155
|
+
true
|
|
156
|
+
rescue Errno::EEXIST
|
|
157
|
+
false
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
break unless idx
|
|
161
|
+
|
|
162
|
+
manifests = Marshal.load(File.binread(File.join(
|
|
163
|
+
config_dir, "._suma_batch_\#{idx}.bin")))
|
|
164
|
+
manifests.each_with_index do |manifest, j|
|
|
165
|
+
one_path = File.join(config_dir,
|
|
166
|
+
"._suma_staged_b\#{idx}_\#{j}.yml")
|
|
167
|
+
out = File.join(out_dir, "._staged_out_b\#{idx}_\#{j}")
|
|
168
|
+
t0 = Time.now
|
|
169
|
+
begin
|
|
170
|
+
File.write(one_path, YAML.dump(manifest))
|
|
171
|
+
compile = { install_fonts: false }
|
|
172
|
+
compile[:novalid] = true if no_valid == "1"
|
|
173
|
+
Metanorma::Collection.parse(one_path).render(
|
|
174
|
+
format: [:xml], output_folder: out,
|
|
175
|
+
preserve_unresolved: true, artifact_store_dir: store_dir,
|
|
176
|
+
compile: compile,
|
|
177
|
+
)
|
|
178
|
+
warn format("[staged worker %d] batch %d/%d member %d OK (%ds)",
|
|
179
|
+
Process.pid, idx + 1, count, j, Time.now - t0)
|
|
180
|
+
ensure
|
|
181
|
+
File.unlink(one_path) rescue nil
|
|
182
|
+
FileUtils.rm_rf(out) rescue nil
|
|
183
|
+
GC.start
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
claimed += 1
|
|
187
|
+
end
|
|
188
|
+
exit 1 if claimed.zero? && count.positive?
|
|
189
|
+
RUBY
|
|
190
|
+
end
|
|
191
|
+
|
|
64
192
|
def manifest
|
|
65
193
|
@manifest ||= YAML.load_file(@config_path)
|
|
66
194
|
end
|
data/lib/suma/term_extractor.rb
CHANGED
|
@@ -94,7 +94,9 @@ module Suma
|
|
|
94
94
|
section_ref = get_section_ref(schema)
|
|
95
95
|
|
|
96
96
|
Glossarist::ManagedConceptCollection.new.tap do |collection|
|
|
97
|
-
|
|
97
|
+
# expressir leaves collection attributes nil when the clause is
|
|
98
|
+
# absent; guard every walk over parsed-model collections.
|
|
99
|
+
Array(schema.entities).each do |entity|
|
|
98
100
|
managed_data = build_managed_data(schema, entity, section_ref,
|
|
99
101
|
classification)
|
|
100
102
|
|
|
@@ -154,7 +156,7 @@ module Suma
|
|
|
154
156
|
data.sources = [build_source_ref(schema, entity)]
|
|
155
157
|
|
|
156
158
|
notes = NoteProcessor.call(
|
|
157
|
-
entity.remarks,
|
|
159
|
+
Array(entity.remarks),
|
|
158
160
|
definitions: data.definition,
|
|
159
161
|
xref_to_mention: method(:xref_to_mention),
|
|
160
162
|
)
|
|
@@ -273,12 +275,16 @@ module Suma
|
|
|
273
275
|
|
|
274
276
|
entity_ref = urn_mention(term_urn("express-language.entity"), "entity")
|
|
275
277
|
|
|
276
|
-
|
|
278
|
+
# expressir 2.4.44 returns nil for entities without SUBTYPE_OF
|
|
279
|
+
# (older versions returned []); guard both shapes.
|
|
280
|
+
subtypes = entity.subtype_of || []
|
|
281
|
+
|
|
282
|
+
if subtypes.empty?
|
|
277
283
|
"#{entity_type} " \
|
|
278
284
|
"that represents the " \
|
|
279
285
|
"#{entity_name_to_text(entity.id)} #{entity_ref}"
|
|
280
286
|
else
|
|
281
|
-
entity_subtypes =
|
|
287
|
+
entity_subtypes = subtypes.map do |e|
|
|
282
288
|
urn_mention(express_entity_urn("#{schema.id}.#{e.id}"), e.id)
|
|
283
289
|
end
|
|
284
290
|
|
data/lib/suma/urn_resolver.rb
CHANGED
|
@@ -96,7 +96,7 @@ module Suma
|
|
|
96
96
|
artifacts = {}
|
|
97
97
|
schemas.each do |schema|
|
|
98
98
|
artifacts[compose(:tech, schema.id)] = schema_payload(schema)
|
|
99
|
-
schema.children.each do |element|
|
|
99
|
+
Array(schema.children).each do |element|
|
|
100
100
|
payload = element_payload(schema, element)
|
|
101
101
|
artifacts[compose(:tech, "#{schema.id}.#{element.id}")] = payload if
|
|
102
102
|
payload
|
|
@@ -111,7 +111,7 @@ module Suma
|
|
|
111
111
|
def add_attribute_artifacts(artifacts, schema, element)
|
|
112
112
|
return unless element.is_a?(Expressir::Model::Declarations::Entity)
|
|
113
113
|
|
|
114
|
-
element.attributes.each do |attribute|
|
|
114
|
+
Array(element.attributes).each do |attribute|
|
|
115
115
|
key = compose(:tech,
|
|
116
116
|
"#{schema.id}.#{element.id}.#{attribute.id}")
|
|
117
117
|
artifacts[key] = attribute_artifact(schema, element, attribute)
|
|
@@ -150,7 +150,7 @@ module Suma
|
|
|
150
150
|
"interface" => Array(schema.interfaces).filter_map do |interface|
|
|
151
151
|
interface.schema&.id
|
|
152
152
|
end,
|
|
153
|
-
"elements" => schema.children.filter_map do |element|
|
|
153
|
+
"elements" => Array(schema.children).filter_map do |element|
|
|
154
154
|
element.id if declared_element?(element)
|
|
155
155
|
end.sort,
|
|
156
156
|
}
|
data/lib/suma/version.rb
CHANGED
data/lib/suma/worker_pool.rb
CHANGED
|
@@ -45,7 +45,11 @@ module Suma
|
|
|
45
45
|
item, index = pair
|
|
46
46
|
begin
|
|
47
47
|
results[index] = block.call(item)
|
|
48
|
-
|
|
48
|
+
# SystemStackError is not StandardError: deep per-item recursion
|
|
49
|
+
# (a pathological EXPRESS AST in the export pretty-printer)
|
|
50
|
+
# overflows the ~1 MB thread stack, and swallowing it would
|
|
51
|
+
# leave silent holes in results. The caller falls back.
|
|
52
|
+
rescue SystemStackError, StandardError => e
|
|
49
53
|
errors << e
|
|
50
54
|
break
|
|
51
55
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: suma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: expressir
|