From 6754bc278d968e4b22648a675e207a4a6885ec45 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Mon, 9 Jan 2023 12:02:56 +0100 Subject: [PATCH 1/2] chore(refactor) make submodules part of copas module the submodules are now lazy-loaded upon first use. --- docs/manual.html | 6 ++---- docs/reference.html | 10 +++++----- misc/testasyncspeed.lua | 2 +- src/copas.lua | 27 ++++++++++++++++++++++----- src/copas/queue.lua | 4 ++-- tests/http-timeout.lua | 2 +- tests/httpredirect.lua | 2 +- tests/lock.lua | 2 +- tests/queue.lua | 2 +- tests/request.lua | 2 +- tests/semaphore.lua | 2 +- tests/timer.lua | 2 +- 12 files changed, 39 insertions(+), 24 deletions(-) diff --git a/docs/manual.html b/docs/manual.html index 76f28a7..e03afb3 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -345,10 +345,9 @@

Creating timers

Below an example of a timer;

 local copas = require("copas")
-local timer = require("copas.timer")
 
 copas(function()
-   timer.new({
+   copas.timer.new({
      delay = 1,                        -- delay in seconds
      recurring = true,                 -- make the timer repeat
      params = "hello world",
@@ -370,9 +369,8 @@ 

Synchronization primitives

 local copas = require("copas")
-local new_lock = require("copas.lock").new
 
-local lock = new_lock()
+local lock = copas.lock.new()
 
 local function some_func()
   local ok, err, wait = lock:get()
diff --git a/docs/reference.html b/docs/reference.html
index 1f68202..31812f8 100644
--- a/docs/reference.html
+++ b/docs/reference.html
@@ -427,7 +427,7 @@ 

Non-blocking data exchange and timer/sleep functions

be "timeout". -
lock.new([timeout], [not_reentrant])
+
copas.lock.new([timeout], [not_reentrant])
Creates and returns a new lock. The timeout specifies the default timeout for the lock in seconds, and defaults to 10 (set it to math.huge to wait forever). By default the lock is re-entrant, @@ -476,10 +476,10 @@

Non-blocking data exchange and timer/sleep functions

queue.name
-
A field set to name of the queue. See queue.new(). +
A field set to name of the queue. See copas.queue.new().
-
queue.new([options])
+
copas.queue.new([options])
Creates and returns a new queue. The options table has the following fields:
    @@ -535,7 +535,7 @@

    Non-blocking data exchange and timer/sleep functions

    maximum. In that case the result will be nil + "too many".
-
semaphore.new(max, [start], [timeout])
+
copas.semaphore.new(max, [start], [timeout])
Creates and returns a new semaphore (fifo). max specifies the maximum number of resources the semaphore can hold. The optional start parameter (default 0) specifies the number of resources upon creation. @@ -556,7 +556,7 @@

Non-blocking data exchange and timer/sleep functions

resources are requested than maximum available the error will be "too many".
-
timer.new(options)
+
copas.timer.new(options)
Creates and returns an (armed) timer object. The options table has the following fields;
  • options.name (optional): the name for the timer, the default name will be diff --git a/misc/testasyncspeed.lua b/misc/testasyncspeed.lua index 1124ac3..37eea76 100644 --- a/misc/testasyncspeed.lua +++ b/misc/testasyncspeed.lua @@ -1,6 +1,6 @@ local copas = require("copas") local synchttp = require("socket.http").request -local asynchttp = require("copas.http").request +local asynchttp = copas.http.request local gettime = require("socket").gettime local targets = { diff --git a/src/copas.lua b/src/copas.lua index 7da2076..f2c8f2d 100644 --- a/src/copas.lua +++ b/src/copas.lua @@ -92,11 +92,28 @@ do end -local copas = setmetatable({},{ - __call = function(self, ...) - return self.loop(...) - end, -}) +-- Setup the Copas meta table to auto-load submodules and define a default method +local copas do + local submodules = { "ftp", "http", "lock", "queue", "semaphore", "smtp", "timer" } + for i, key in ipairs(submodules) do + submodules[key] = true + submodules[i] = nil + end + + copas = setmetatable({},{ + __index = function(self, key) + if submodules[key] then + self[key] = require("copas."..key) + submodules[key] = nil + return rawget(self, key) + end + end, + __call = function(self, ...) + return self.loop(...) + end, + }) +end + -- Meta information is public even if beginning with an "_" copas._COPYRIGHT = "Copyright (C) 2005-2013 Kepler Project, 2015-2023 Thijs Schreijer" diff --git a/src/copas/queue.lua b/src/copas/queue.lua index e63f6f1..01cd53c 100644 --- a/src/copas/queue.lua +++ b/src/copas/queue.lua @@ -1,6 +1,6 @@ local copas = require "copas" -local Sema = require "copas.semaphore" -local Lock = require "copas.lock" +local Sema = copas.semaphore +local Lock = copas.lock local Queue = {} diff --git a/tests/http-timeout.lua b/tests/http-timeout.lua index 48be63a..5379b4b 100644 --- a/tests/http-timeout.lua +++ b/tests/http-timeout.lua @@ -6,7 +6,7 @@ local copas = require 'copas' local socket = require 'socket' local ltn12 = require 'ltn12' -local request = require("copas.http").request +local request = copas.http.request -- copas.debug.start() diff --git a/tests/httpredirect.lua b/tests/httpredirect.lua index b82a62d..ad2bb24 100644 --- a/tests/httpredirect.lua +++ b/tests/httpredirect.lua @@ -1,7 +1,7 @@ -- test redirecting http <-> https combinations local copas = require("copas") -local http = require("copas.http") +local http = copas.http local ltn12 = require("ltn12") local dump_all_headers = false local redirect diff --git a/tests/lock.lua b/tests/lock.lua index c2a5250..0754053 100644 --- a/tests/lock.lua +++ b/tests/lock.lua @@ -4,7 +4,7 @@ package.path = string.format("../src/?.lua;%s", package.path) local copas = require "copas" -local Lock = require "copas.lock" +local Lock = copas.lock local gettime = require("socket").gettime local test_complete = false diff --git a/tests/queue.lua b/tests/queue.lua index 9c6e5c9..2e6d71f 100644 --- a/tests/queue.lua +++ b/tests/queue.lua @@ -4,7 +4,7 @@ local now = require("socket").gettime local copas = require "copas" -local Queue = require "copas.queue" +local Queue = copas.queue diff --git a/tests/request.lua b/tests/request.lua index 3d73135..a8c0549 100644 --- a/tests/request.lua +++ b/tests/request.lua @@ -1,5 +1,5 @@ local copas = require("copas") -local http = require("copas.http") +local http = copas.http local url = assert(arg[1], "missing url argument") local debug_mode = not not arg[2] diff --git a/tests/semaphore.lua b/tests/semaphore.lua index 6559a43..e922014 100644 --- a/tests/semaphore.lua +++ b/tests/semaphore.lua @@ -4,7 +4,7 @@ local now = require("socket").gettime local copas = require "copas" -local semaphore = require "copas.semaphore" +local semaphore = copas.semaphore diff --git a/tests/timer.lua b/tests/timer.lua index a39bf8a..833db4a 100644 --- a/tests/timer.lua +++ b/tests/timer.lua @@ -5,7 +5,7 @@ package.path = string.format("../src/?.lua;%s", package.path) local copas = require "copas" local gettime = require("socket").gettime -local timer = require "copas.timer" +local timer = copas.timer local successes = 0 From 9d1f8d2c5a5ec4d0ee52da89171370901726d1d6 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Mon, 9 Jan 2023 12:07:40 +0100 Subject: [PATCH 2/2] feat(cli) add a runtime script to run code in a copas environment the copas runtime script cli mimics the Lua cli. --- bin/copas.lua | 85 ++++++++++++++++++++++++++++++++++++++++++++ copas-cvs-6.rockspec | 5 +++ docs/index.html | 2 ++ docs/manual.html | 25 +++++++++++++ 4 files changed, 117 insertions(+) create mode 100755 bin/copas.lua diff --git a/bin/copas.lua b/bin/copas.lua new file mode 100755 index 0000000..c0da530 --- /dev/null +++ b/bin/copas.lua @@ -0,0 +1,85 @@ +#!/usr/bin/env lua + +-- luacheck: globals copas +copas = require("copas") + + +-- Error handler that forces an application exit +local function errorhandler(err, co, skt) + io.stderr:write(copas.gettraceback(err, co, skt).."\n") + os.exit(1) +end + + +local function version_info() + print(copas._VERSION, copas._COPYRIGHT) + print(copas._DESCRIPTION) + print("Lua VM:", _G._VERSION) +end + + +local function load_lib(lib_name) + require(lib_name) +end + + +local function run_code(code) + if loadstring then -- deprecated in Lua 5.2 + assert(loadstring(code, "command line"))() + else + assert(load(code, "command line"))() + end +end + + +local function run_stdin() + assert(loadfile())() +end + + +local function run_file(filename, i) + -- shift arguments, such that the Lua file being executed is at index 0. The + -- first argument following the name is at index 1. + local last = #arg + local first = #arg + for idx, v in pairs(arg) do + if idx < first then first = idx end + end + for n = first - i, last do + arg[n] = arg[n+i] -- luacheck: ignore + end + assert(loadfile(filename))() +end + + +local function show_usage() + print([[ +usage: copas [options]... [script [args]...]. +Available options are: + -e chunk Execute string 'chunk'. + -l name Require library 'name'. + -v Show version information. + -- Stop handling options. + - Execute stdin and stop handling options.]]) + os.exit(1) +end + + +copas(function() + copas.seterrorhandler(errorhandler) + local i = 0 + while i < math.max(#arg, 1) do -- if no args, use 1 being 'nil' + i = i + 1 + local handled = false + local opt = arg[i] or "-" -- set default action if no args + -- options to continue handling + if opt == "-v" then version_info() handled = true end + if opt == "-l" then i = i + 1 load_lib(arg[i]) handled = true end + if opt == "-e" then i = i + 1 run_code(arg[i]) handled = true end + -- options that terminate handling + if opt == "--" then return end + if opt == "-" then return run_stdin() end + if opt:sub(1,1) == "-" and not handled then return show_usage() end + if not handled then return run_file(opt, i) end + end +end) diff --git a/copas-cvs-6.rockspec b/copas-cvs-6.rockspec index 068b292..7c4cc9c 100644 --- a/copas-cvs-6.rockspec +++ b/copas-cvs-6.rockspec @@ -35,6 +35,11 @@ dependencies = { } build = { type = "builtin", + install = { + bin = { + copas = "bin/copas.lua", + } + }, modules = { ["copas"] = "src/copas.lua", ["copas.http"] = "src/copas/http.lua", diff --git a/docs/index.html b/docs/index.html index 35d1e95..ce1e5f1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -104,6 +104,8 @@

    History

    • Fix: windows makefile didn't include all submodules.
    • Fix: creating a new timer with a bad delay setting would throw a bad error message.
    • +
    • Refactor: submodules are now part of copas (lazily loaded) and do not need to be required anymore
    • +
    • Feat: runtime script added to directly run copas based code
    Copas 4.6.0 [30/Dec/2022]
    diff --git a/docs/manual.html b/docs/manual.html index e03afb3..d238aa1 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -55,6 +55,31 @@

    Installing

    Note: LuaSec is not automatically installed as a dependency. If you want to use ssl with Copas, you need to manually install LuaSec as well.

    +

    Runtime

    + +Copas can either be used as a regular Lua library, or as a runtime. A command line script that +acts as a runtime engine is included. + +When using the runtime, the library is available as a global (copas), and the +scheduler will automatically be started. For example: + +
    +  #!/usr/bin/env copas
    +
    +  local count = 0
    +  copas.timer.new {
    +    delay = 1,
    +    recurring = true,
    +    callback = function(self)
    +      count = count + 1
    +      print('hello world ' .. count)
    +      if count >= 5 then
    +        self:cancel()
    +      end
    +    end
    +  }
    +
    +

    Introduction to Copas