From b4268bc862fe07fa7b59ddef70560daa0eecdb54 Mon Sep 17 00:00:00 2001 From: Luis Mondesi Date: Fri, 17 Oct 2025 11:24:38 -0400 Subject: [PATCH 1/2] fix: example debug --- share/vim/skeleton.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/share/vim/skeleton.py b/share/vim/skeleton.py index 0efcf06..e05c077 100644 --- a/share/vim/skeleton.py +++ b/share/vim/skeleton.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 """ -$Revision: 1.1.0 $ -$Date: 2025-10-08 22:35 EDT $ my_name < email@example.com > DESCRIPTION: @@ -10,6 +8,8 @@ USAGE: skeleton.py 1 2 --required foo --sum --move paper --workers 8 --fail-prob 0.3 + +LICENSE: ____ """ import argparse @@ -34,6 +34,7 @@ def parse_args(argv=None): help='probability a task raises (0.0–1.0)') p.add_argument('--seed', type=int, default=None, help='random seed for reproducibility') p.add_argument('-v', '--verbose', action='store_true', help='verbose output') + p.add_argument('-d', '--debug', action='store_true', help='debug output') return p.parse_args(argv) # --- workload --------------------------------------------------------------- From 0b818e5c2c3608018f28815b67af3449052696d2 Mon Sep 17 00:00:00 2001 From: Luis Mondesi Date: Fri, 17 Oct 2025 11:24:45 -0400 Subject: [PATCH 2/2] feat: better ruby example --- share/vim/skeleton.rb | 306 +++++++++++++++++++++++++++++------------- 1 file changed, 210 insertions(+), 96 deletions(-) diff --git a/share/vim/skeleton.rb b/share/vim/skeleton.rb index 488c619..e1933da 100755 --- a/share/vim/skeleton.rb +++ b/share/vim/skeleton.rb @@ -1,98 +1,26 @@ #!/usr/bin/env ruby - +# frozen_string_literal: true =begin -$Revision: 1.0.1 $ -$Date: 2011-08-16 21:31 EDT $ my_name < email@example.com > DESCRIPTION: -USAGE: skeleton --help -LICENSE: ___ -=end - -require 'optparse' -require 'ostruct' - -VERSION="0.0.1" - -# The options specified on the command line will be collected in *options*. -# We set default values here. -options = OpenStruct.new -# options.library = [] -options.verbose = 0 # levels 0 - 10 -options.debug = false - -opts = OptionParser.new do |o| - o.banner = "Usage: #{File.basename $0} [options]" - - o.separator "" - o.separator "Specific options:" - - # Mandatory argument. - # o.on("-r", "--require LIBRARY", - # "Require the LIBRARY before executing your script") do |lib| - # options.library << lib - # end - - # Optional argument; multi-line description. - #o.on("-i", "--inplace [EXTENSION]", - - # # Cast 'time' argument to a Time object. - # o.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time| - # options.time = time - # end - - # # List of arguments. - # o.on("--list x,y,z", Array, "Example 'list' of arguments") do |list| - # options.list = list - # end - - # # Optional argument with keyword completion. - # o.on("--type [TYPE]", [:text, :binary, :auto], - # "Select transfer type (text, binary, auto)") do |t| - # options.transfer_type = t - # end - - # Boolean switch. - o.on("-v", "--[no-]verbose", "Run verbosely. Increase level of verbosity by using multiple -v") do |v| - options.verbose += 1 - end - o.on("-D", "--[no-]debug", "Show debug messages") do |v| - options.debug = v - options.verbose += 10 - end + Demo skeleton showing ensure-style thread cleanup: + submit N items, always wait for completion, collect results & errors. - o.separator "" - o.separator "Common options:" +USAGE: + ruby skeleton.rb 1 2 --required foo --sum --move paper --workers 8 --fail-prob 0.3 - # No argument, shows at tail. This will print an options summary. - # Try it and see! - o.on_tail("-h", "--help", "Show this message") do - puts opts - exit - end +LICENSE: ____ +=end - # Another typical switch to print the version. - o.on_tail("--version", "Show version") do - puts VERSION - exit - end -end +require 'optparse' +require 'etc' +# require 'ostruct' -begin - opts.parse!(ARGV) -rescue OptionParser::MissingArgument => e - $stderr.puts e.message - puts opts - exit 1 -rescue => e - $stderr.puts "ERROR: #{e.class} #{e.message}" - exit 2 -end +VERSION="0.0.2" # helpers class MyError < StandardError; end - class MyExample def initialize(opts={}) @debug = opts[:debug] @@ -106,12 +34,15 @@ def scolor(msg,color) 'green' => "\033[0;32m", 'blue' => "\033[0;34m" } - if STDOUT.tty? - "#{colors[color.downcase]}#{msg}#{colors['norm']}" + # honor the TTY of the stream we actually use for these messages (stderr) + if $stderr.tty? || $stdout.tty? + code = colors[color.downcase] || colors['norm'] + "#{code}#{msg}#{colors['norm']}" else msg end end + def debug(*msg) return if not @debug @@ -123,15 +54,18 @@ def debug(*msg) $stderr.puts "#{scolor(msg.join(' '), 'blue')}" end end + def info(*msg) prefix = 'INFO: ' unless msg[0] =~ /^\s*INFO:/ $stderr.puts scolor("#{prefix}#{msg.join(' ')}", "blue") end alias warn info + def verbose(msg,level=1) return if @verbose <= 0 puts "#{msg}" if @verbose >= level end + def error(*msg) prefix = 'ERROR: ' unless msg[0] =~ /^\s*ERROR:/ $stderr.puts scolor("#{prefix}#{msg.join(' ')}", "red") @@ -139,16 +73,155 @@ def error(*msg) end # end helpers -# main() +# --- CLI parsing ------------------------------------------------------------- + +def parse_args(argv = ARGV) + options = { + accumulate: :max, + move: nil, + required: nil, + workers: nil, + fail_prob: 0.30, + seed: nil, + verbose: 0, # verbosity level: 0,1,2,... + debug: ENV.key?('DEBUG') ? true : false, + integers: [] + } + + parser = OptionParser.new do |op| + op.banner = "Usage: #{File.basename($PROGRAM_NAME)} N [N ...] [options]" + op.separator "" + op.separator "Options:" + + op.on('--sum', 'Sum the integers (default: find the max)') { options[:accumulate] = :sum } + op.on('--move MOVE', %w[rock paper scissors], 'Choose: rock | paper | scissors') { |m| options[:move] = m } + op.on('--required VAL', 'A required option') { |v| options[:required] = v } + op.on('--workers N', Integer, 'Max worker threads (default: auto)') { |n| options[:workers] = n } + op.on('--fail-prob P', Float, 'Probability a task raises (0.0–1.0)') { |p| options[:fail_prob] = p } + op.on('--seed SEED', Integer, 'Random seed for reproducibility') { |s| options[:seed] = s } + # -v can be repeated: -v, -vv, -vvv... + op.on('-v', '--verbose', 'Increase verbosity (repeat for more)') { options[:verbose] += 1 } + op.on('-h', '--help', 'Show this help and exit') { puts op; exit 0 } + op.on('-D', '--debug', 'Show debug messages') { options[:debug] = true; options[:verbose] = 10 } + op.on_tail('--version', 'Show version') do + puts VERSION + exit + end + end + + # Capture positional integers + begin + parser.parse!(argv) + rescue OptionParser::MissingArgument => e + $stderr.puts e.message + puts parser + exit 1 + rescue => e + $stderr.puts "ERROR: #{e.class} #{e.message}" + exit 2 + end + + options[:integers] = argv.map do |tok| + Integer(tok) + rescue ArgumentError + warn "Non-integer positional argument: #{tok.inspect}" + exit 2 + end + + if options[:integers].empty? + warn "At least one positional integer N is required.\n\n#{parser}" + exit 2 + end + + if options[:required].nil? + warn "--required is mandatory.\n\n#{parser}" + exit 2 + end + + if options[:fail_prob] < 0.0 || options[:fail_prob] > 1.0 + warn "--fail-prob must be between 0.0 and 1.0 (got #{options[:fail_prob]})" + exit 2 + end + + options +end + +# --- workload --------------------------------------------------------------- + +def handle(item, fail_prob: 0.3) + # Simulate work; sometimes fail. + sleep(rand(0.05..0.25)) + raise "boom on #{item}" if rand < fail_prob + "processed #{item}" +end + +# --- orchestration with ensure-style cleanup -------------------------------- + +def process_all(items, max_workers: nil, fail_prob: 0.3, verbose: false) + results = [] + errors = [] -begin - str = "Hello" - val = "World" + # Choose worker count: user-specified, else number of processors, else 4. + worker_count = (max_workers && max_workers > 0) ? max_workers : (Etc.respond_to?(:nprocessors) ? Etc.nprocessors : 4) + worker_count = 1 if worker_count < 1 - obj = MyExample.new debug: options.debug, verbose: options.verbose + job_queue = Queue.new + items.each { |it| job_queue << it } + + results_lock = Mutex.new + errors_lock = Mutex.new + + threads = [] + + begin + worker_count.times do + threads << Thread.new do + loop do + item = nil + begin + item = job_queue.pop(true) # non-blocking; raises ThreadError when empty + rescue ThreadError + break + end + + begin + out = handle(item, fail_prob: fail_prob) + results_lock.synchronize do + results << out + puts "[ok] #{out}" if verbose + end + rescue => e + # Build a traceback-like string + tb = e.full_message(highlight: false, order: :top) + errors_lock.synchronize do + errors << [item, e, tb] + if verbose + puts "[err] item=#{item} err=#{e}" + puts tb + end + end + end + end + end + end + ensure + # ensure-style: always join threads and tear down + threads.each(&:join) + end + + [results, errors] +end + +# --- main ------------------------------------------------------------------- + +def main(argv = ARGV) + args = parse_args(argv) + + obj = MyExample.new debug: args[:debug], verbose: args[:verbose] + $stdout.sync = true # demonstrates debug: - obj.debug(str, val, 'extra') + obj.debug('hello', 'world', 'extra') obj.error('this', 'is', 'error', 'with', 'extra') obj.info('this', 'is', 'info', 'with', 'extra') obj.info('this is also info with', 'extra') @@ -157,10 +230,51 @@ def error(*msg) obj.verbose("printing verbose message level 1") obj.verbose("printing verbose message level 2", 2) - puts "sample" unless $_testing + # Seed RNG for reproducibility if requested + srand(args[:seed]) if args[:seed] + + begin + sum_or_max = + if args[:accumulate] == :sum + args[:integers].sum + else + args[:integers].max + end + + items = (0...sum_or_max).to_a + + results, errors = process_all( + items, + max_workers: args[:workers], + fail_prob: args[:fail_prob], + verbose: args[:verbose] > 0 + ) + + puts "Results:" + results.each { |r| puts " #{r}" } + + puts "\nErrors:" + errors.each do |item, exc, tb| + puts " item=#{item} err=#{exc}" + puts tb if args[:verbose] > 0 + end + + if args[:move] + puts "\nYou played: #{args[:move]}" + end + + exit(errors.empty? ? 0 : 1) + + rescue Interrupt + warn "\nInterrupted by user." + exit 130 + rescue => e + warn "ERROR: #{e}" + warn e.full_message(highlight: false, order: :top) + exit 1 + end +end - # demonstrates raising/throwing errors - raise MyError, "Too many repetitions" if options.list and options.list.size > 10 -rescue MyError => e - error e.message +if __FILE__ == $PROGRAM_NAME + main end