From 452520add4170799824c8be3985928457fac74bb Mon Sep 17 00:00:00 2001 From: pbauer Date: Thu, 8 Nov 2018 11:31:56 +0900 Subject: [PATCH] [fc] Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-27T22:44:03-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/e4b67fcc6dc3785f5d35b3cc20dd19ed3680feef Experiments Files changed: A src/plone/recipe/zope2instance/wsgischema.xml A src/plone/recipe/zope2instance/zopectl.py M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-28T11:48:49-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/56032ac90b5bd089dd7ec1dc04077d34c7a7c8fb Fix adduser and run Files changed: M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/zopectl.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-28T12:46:58-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/bfbd34b50874aa50eff594e4ffb3365389528778 Take logger schema back out; logging gets configured via the wsgi server Files changed: M src/plone/recipe/zope2instance/wsgischema.xml M src/plone/recipe/zope2instance/zopectl.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-28T13:35:39-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/e4610382ccbf0d7d1b25ae86041bf17dec3d3113 generate wsgi.ini Files changed: M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-28T21:27:31-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/410f0bb52fe1a8f3b36afd959671912b7cf5aa2f docs, determine wsgi option based on presense of ZServer Files changed: M CHANGES.rst M README.rst M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-10-28T21:42:26-04:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/63b1a7485c47d0e4f6d7d5e533bff4fe6914092d Back to explicit option Files changed: M CHANGES.rst M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:02+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/8bbcef4fa2910d930f776a489c393efb4ec89912 Experiments Files changed: A src/plone/recipe/zope2instance/wsgischema.xml A src/plone/recipe/zope2instance/zopectl.py M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:02+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/2f4ed5a28e1f9e619d769d6fe2e5c13a5d55f076 Fix adduser and run Files changed: M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/zopectl.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:02+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/d8e6e60b50fc7a630ee274dd5841758789434699 Take logger schema back out; logging gets configured via the wsgi server Files changed: M src/plone/recipe/zope2instance/wsgischema.xml M src/plone/recipe/zope2instance/zopectl.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:02+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/8e801beadfe0c33f669570a32d40f13b605ddd6f generate wsgi.ini Files changed: M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:38+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/d2e51d8d2fe80cdcd03afc799b47318f65edc6ed docs, determine wsgi option based on presense of ZServer Files changed: M CHANGES.rst M README.rst M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-07T12:54:38+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/f612e50c6e2215769e61fb0249eb9557e892e465 Back to explicit option Files changed: M CHANGES.rst M src/plone/recipe/zope2instance/recipe.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-08T07:24:54+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/efcd9f8fc26d04e230618dfd5cfd0988f866fe3c Merge remote-tracking branch 'remotes/origin/wsgi-instance-script' into wsgi-instance-script Files changed: Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-08T07:31:10+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/492883ee6b69962bc4e7ddabc2015b4406621611 Consolidate ctl and zopectl Files changed: M src/plone/recipe/zope2instance/ctl.py D src/plone/recipe/zope2instance/zopectl.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-08T07:52:31+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/e0f49ea0f213e93e06d3b60ce56652a70d39c10f Copy zope.conf schema tests from ZServer Files changed: A src/plone/recipe/zope2instance/tests/test_wsgischema.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-08T09:03:11+09:00 Author: David Glick (davisagli) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/5b5744188c224624498806954cc0c11c249d81a2 Fix test Files changed: M src/plone/recipe/zope2instance/tests/test_wsgischema.py Repository: plone.recipe.zope2instance Branch: refs/heads/master Date: 2018-11-08T11:31:56+09:00 Author: Philip Bauer (pbauer) Commit: https://github.com/plone/plone.recipe.zope2instance/commit/cff33b5c9b69b043b2c57dbb5efbdd3ece4cba0d Merge pull request #52 from plone/wsgi-instance-script Build a bin/instance script that works with Zope 4 + WSGI Files changed: A src/plone/recipe/zope2instance/tests/test_wsgischema.py A src/plone/recipe/zope2instance/wsgischema.xml M CHANGES.rst M README.rst M src/plone/recipe/zope2instance/ctl.py M src/plone/recipe/zope2instance/recipe.py --- last_commit.txt | 275 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 255 insertions(+), 20 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index ef5dcd5f40..f1d6e01704 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,48 +1,283 @@ -Repository: plone.namedfile +Repository: plone.recipe.zope2instance Branch: refs/heads/master -Date: 2018-11-08T08:18:56+09:00 -Author: Philip Bauer (pbauer) -Commit: https://github.com/plone/plone.namedfile/commit/0e23a773ae5d0852c3b721f553cd4fb419b2f3df +Date: 2018-10-27T22:44:03-04:00 +Author: David Glick (davisagli) +Commit: https://github.com/plone/plone.recipe.zope2instance/commit/e4b67fcc6dc3785f5d35b3cc20dd19ed3680feef -add forgotten change to BytesIO +Experiments Files changed: -M plone/namedfile/utils/__init__.py +A src/plone/recipe/zope2instance/wsgischema.xml +A src/plone/recipe/zope2instance/zopectl.py +M src/plone/recipe/zope2instance/ctl.py +M src/plone/recipe/zope2instance/recipe.py -b"diff --git a/plone/namedfile/utils/__init__.py b/plone/namedfile/utils/__init__.py\nindex 9c7ddad..45fcb3f 100644\n--- a/plone/namedfile/utils/__init__.py\n+++ b/plone/namedfile/utils/__init__.py\n@@ -268,15 +268,18 @@ def rotate_image(image_data, method=None, REQUEST=None):\n del(exif_data['Exif'][piexif.ExifIFD.SceneType])\n # This Element piexif.ExifIFD.SceneType cause error on dump\n exif_bytes = piexif.dump(exif_data)\n- output_image_data = StringIO()\n+\n+ output_image_data = BytesIO()\n img.save(output_image_data, format=fmt, exif=exif_bytes)\n width, height = img.size\n return output_image_data.getvalue(), width, height, exif_data\n \n+\n @deprecate('use getHighPixelDensityScales instead')\n def getRetinaScales():\n return getHighPixelDensityScales()\n \n+\n def getHighPixelDensityScales():\n registry = queryUtility(IRegistry)\n if IImagingSchema and registry:\n" +b'diff --git a/src/plone/recipe/zope2instance/ctl.py b/src/plone/recipe/zope2instance/ctl.py\nindex de0af72..65f0989 100644\n--- a/src/plone/recipe/zope2instance/ctl.py\n+++ b/src/plone/recipe/zope2instance/ctl.py\n@@ -28,7 +28,7 @@\n """\n \n from pkg_resources import iter_entry_points\n-from ZServer.Zope2.Startup import zopectl\n+from . import zopectl\n \n import csv\n import os\n@@ -50,6 +50,12 @@\n \'ERROR: You are not member of the "Administrators" group, \'\n \'or you have not run the shell as Administrator.\')\n \n+try:\n+ import ZServer\n+ HAS_ZSERVER = True\n+except ImportError:\n+ HAS_ZSERVER = False\n+\n \n class AdjustedZopeCmd(zopectl.ZopeCmd):\n \n@@ -422,14 +428,24 @@ def get_startup_cmd(self, python, more, pyflags=""):\n # will act as escapes. Use r\'\' instead.\n # Also, don\'t forget that \'python\'\n # may have spaces and needs to be quoted.\n+ if HAS_ZSERVER:\n+ cmd = (\n+ "from Zope2 import configure; "\n+ "configure(r\'%s\'); "\n+ "import Zope2; app=Zope2.app(); "\n+ )\n+ else:\n+ cmd = (\n+ "from Zope2.Startup.run import configure_wsgi; "\n+ "configure_wsgi(r\'%s\'); "\n+ "import Zope2; app=Zope2.app(); "\n+ )\n cmdline = (\n- \'"%s" %s "%s" %s -c "from Zope2 import configure; \'\n- \'configure(r\\\'%s\\\'); \'\n- \'import Zope2; app=Zope2.app(); \' % (\n+ \'"%s" %s "%s" %s -c "%s\' % (\n python, pyflags,\n self.options.interpreter,\n pyflags,\n- self.options.configfile\n+ cmd % self.options.configfile,\n )\n )\n \n@@ -574,7 +590,7 @@ def do_foreground(self, arg, debug=True):\n local_additions = []\n \n if debug:\n- if not program.count(\'-X\'):\n+ if HAS_ZSERVER and not program.count(\'-X\'):\n local_additions += [\'-X\']\n if not program.count(\'debug-mode=on\'):\n local_additions += [\'debug-mode=on\']\n@@ -621,15 +637,25 @@ def main(args=None):\n # Realize arguments and set documentation which is used in the -h option\n options.realize(args, doc=__doc__)\n \n- # Change the program to avoid warning messages\n- startup = os.path.dirname(zopectl.__file__)\n-\n+ # Run the right command depending on whether we have ZServer\n options.interpreter = os.path.join(options.directory, \'bin\', \'interpreter\')\n if sys.platform == \'win32\':\n options.interpreter += \'-script.py\'\n- script = os.path.join(startup, \'run.py\')\n- options.program = [\n- options.python, options.interpreter, script, \'-C\', options.configfile]\n+ if HAS_ZSERVER:\n+ from ZServer.Zope2.Startup import run\n+ script = run.__file__\n+ options.program = [\n+ options.python, options.interpreter, script, \'-C\',\n+ options.configfile\n+ ]\n+ else:\n+ from Zope2.Startup import serve\n+ script = serve.__file__\n+ # @@@ generate this?\n+ wsgi_ini = os.path.join(options.directory, \'etc\', \'waitress.ini\')\n+ options.program = [\n+ options.python, options.interpreter, script, wsgi_ini\n+ ]\n \n # We use our own ZopeCmd set, that is derived from the original one.\n c = AdjustedZopeCmd(options)\ndiff --git a/src/plone/recipe/zope2instance/recipe.py b/src/plone/recipe/zope2instance/recipe.py\nindex 95c9f11..a3fbc48 100644\n--- a/src/plone/recipe/zope2instance/recipe.py\n+++ b/src/plone/recipe/zope2instance/recipe.py\n@@ -610,10 +610,7 @@ def install_scripts(self):\n extra_paths = options.get(\'extra-paths\', \'\').split()\n requirements, ws = self.egg.working_set([\'plone.recipe.zope2instance\'])\n reqs = [self.options.get(\'control-script\', self.name)]\n- if self.wsgi:\n- reqs.extend([\'Zope2.Startup.serve\', \'main\'])\n- else:\n- reqs.extend([\'plone.recipe.zope2instance.ctl\', \'main\'])\n+ reqs.extend([\'plone.recipe.zope2instance.ctl\', \'main\'])\n reqs = [tuple(reqs)]\n \n if options.get(\'relative-paths\'):\n@@ -637,8 +634,7 @@ def __repr__(self):\n )\n \n options[\'zope-conf\'] = zope_conf_path\n- arguments = ["-C", zope_conf_path, \'-p\', program_path] \\\n- if not self.wsgi else [\'ignored\']\n+ arguments = ["-C", zope_conf_path, \'-p\', program_path]\n if zopectl_umask:\n arguments.extend(["--umask", int(zopectl_umask, 8)])\n script_arguments = (\'\\n \' + repr(arguments) +\n@@ -647,10 +643,9 @@ def __repr__(self):\n generated = self._install_scripts(\n options[\'bin-directory\'], ws, reqs=reqs, extra_paths=extra_paths,\n script_arguments=script_arguments)\n- if not self.wsgi:\n- generated.extend(self._install_scripts(\n- os.path.join(options[\'location\'], \'bin\'), ws,\n- interpreter=program_name, extra_paths=extra_paths))\n+ generated.extend(self._install_scripts(\n+ os.path.join(options[\'location\'], \'bin\'), ws,\n+ interpreter=program_name, extra_paths=extra_paths))\n return generated\n \n def _install_scripts(self, dest, working_set, reqs=(), interpreter=None,\n@@ -673,10 +668,7 @@ def _install_scripts(self, dest, working_set, reqs=(), interpreter=None,\n script_arguments=script_arguments,\n )\n else:\n- if self.wsgi:\n- initialization = wsgi_initialization % options\n- else:\n- initialization = options[\'initialization\'] % options\n+ initialization = options[\'initialization\'] % options\n return zc.buildout.easy_install.scripts(\n dest=dest,\n reqs=reqs,\n@@ -1121,9 +1113,3 @@ def render_file_storage(self, file_storage, blob_storage,\n %s\n \n """\n-\n-wsgi_initialization = """\\\n-from Zope2.Startup.run import make_wsgi_app\n-wsgiapp = make_wsgi_app({}, \'%(zope-conf)s\')\n-def application(*args, **kwargs):return wsgiapp(*args, **kwargs)\n-"""\ndiff --git a/src/plone/recipe/zope2instance/wsgischema.xml b/src/plone/recipe/zope2instance/wsgischema.xml\nnew file mode 100644\nindex 0000000..b2aa221\n--- /dev/null\n+++ b/src/plone/recipe/zope2instance/wsgischema.xml\n@@ -0,0 +1,522 @@\n+\n+\n+ \n+\n+ \n+ \n+ \n+ \n+\n+ \n+ \n+ This "logger" type only applies to access and request ("trace")\n+ logging; event logging is handled by the "logging" package in\n+ the Python standard library. The loghandler type used here is\n+ provided by the "ZConfig.components.logger" package.\n+ \n+ \n+ \n+ \n+\n+ \n+ \n+ A section which allows you to define simple key-value pairs which\n+ will be used as environment variable settings during startup.\n+ \n+ \n+ \n+ Use any key/value pair, e.g. \'MY_PRODUCT_ENVVAR foo_bar\'\n+ \n+ \n+ \n+\n+ \n+\n+ \n+ We need to specialize the database configuration section for Zope\n+ only by including a (required) mount-point argument, which\n+ is a string. A Zope ZODB database can have multiple mount points,\n+ so this is a multikey.\n+ \n+ \n+ \n+ The mount point is a slash-separated path to a \n+ \'Products.ZODBMountPoint.Mount.MountPoint\' instance in Zope. If \n+ such an instance exists, it can mount an object (the mounted \n+ object) into Zope.\n+ By default, the object will be mounted at the same path in Zope (i.e.\n+ \'/foo/bar\' in the database will be mounted at \'/foo/bar\' in Zope).\n+\n+ The object can be mounted at a different point using the\n+ \'virtual_path:real_path\' syntax (e.g. \'mount-point /foo/bar:/bar\' \n+ will mount the object at \'/bar\' in the database to \'/foo/bar\' in \n+ Zope). The name of the mount point (\'bar\') must be the same as \n+ the mounted object.\n+\n+ It is also possible to specify the root that should be used in the\n+ mounted database by using the syntax\n+ \'virtual_path:~real_root:real_path\'. The root defaults to \'Application\'\n+ and should not normally be changed.\n+ \n+ \n+\n+ \n+ \n+ Change the connection class a database uses on a per-database basis to\n+ support different connection policies. Use a Python dotted-path\n+ name to specify the connection class.\n+ \n+ \n+\n+ \n+ \n+ Change the class factory function a database uses on a\n+ per-database basis to support different class factory policy.\n+ Use a Python dotted-path name to specify the class factory function.\n+ \n+ \n+\n+ \n+ \n+ Change the container class a (mounted) database uses on a\n+ per-database basis to support a different container than a plain\n+ Folder. Use a Python dotted-path name to specify the container class.\n+ \n+ \n+\n+ \n+\n+ \n+ \n+ This section describes the options for zopectl. These options\n+ have no default value specified in the schema; in some cases,\n+ zopectl calculates a dynamic default, in others, the feature\n+ associated with the option is disabled.\n+\n+ For those options that also have corresponding command-line\n+ options, the command line option (short and long form) are given\n+ here too.\n+ \n+\n+ \n+ \n+ The program(s) that will be run by the runner\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -d or --daemon.\n+\n+ If this option is true, zdrun.py runs in the background as a\n+ true daemon. It forks an child process which becomes the\n+ subprocess manager, while the parent exits (making the shell\n+ that started it believe it is done). The child process also\n+ does the following:\n+\n+ - if the directory option is set, change into that directory\n+\n+ - redirect stdin, stdout and stderr to /dev/null\n+\n+ - call setsid() so it becomes a session leader\n+\n+ - call umask(022)\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -b or --backoff-limit.\n+\n+ When the subprocess crashes, zdrun.py inserts a one-second\n+ delay before it restarts it. When the subprocess crashes\n+ again right away, the delay is incremented by one second, and\n+ so on. What happens when the delay has reached the value of\n+ backoff-limit (in seconds), depends on the value of the\n+ forever option. If forever is false, zdrun.py gives up at\n+ this point, and exits. An always-crashing subprocess will\n+ have been restarted exactly backoff-limit times in this case.\n+ If forever is true, zdrun.py continues to attempt to restart\n+ the process, keeping the delay at backoff-limit seconds.\n+\n+ If the subprocess stays up for more than backoff-limit\n+ seconds, the delay is reset to 1 second.\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -f or --forever.\n+\n+ If this option is true, zdrun.py will keep restarting a\n+ crashing subprocess forever. If it is false, it will give up\n+ after backoff-limit crashes in a row. See the description of\n+ backoff-limit for details.\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -u or --user.\n+\n+ When zdrun.py is started by root, this option specifies the\n+ user as who the the zdrun.py process (and hence the daemon\n+ subprocess) will run. This can be a user name or a numeric\n+ user id. Both the user and the group are set from the\n+ corresponding password entry, using setuid() and setgid().\n+ This is done before zdrun.py does anything else besides\n+ parsing its command line arguments.\n+\n+ NOTE: when zdrun.py is not started by root, specifying this\n+ option is an error. (XXX This may be a mistake.)\n+\n+ XXX The zdrun.py event log file may be opened *before*\n+ setuid() is called. Is this good or bad?\n+ \n+ \n+\n+ \n+ \n+ If this option is true, the zdrun.py process will remain even\n+ when the daemon subprocess is stopped. In this case, zopectl\n+ will restart zdrun.py as necessary. If this option is false,\n+ zdrun.py will exit when the daemon subprocess is stopped\n+ (unless zdrun.py intends to restart it).\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -m or --umask.\n+\n+ When daemon mode is used, this option specifies the octal umask\n+ of the subprocess.\n+ \n+ \n+\n+ \n+ \n+ If this option is true, zopectl enters interactive mode\n+ when it is invoked without a positional command argument. If\n+ it is false, you must use the -i or --interactive command line\n+ option to zopectl to enter interactive mode.\n+ \n+ \n+\n+ \n+ \n+ The prompt shown by zopectl program.\n+ \n+ \n+\n+ \n+ \n+ Command-line option: -s or --socket-name.\n+\n+ The pathname of the Unix domain socket used for communication\n+ between zopectl.py and zdrun.py. The default is relative to the\n+ current directory in which zdctl.py and zdrun.py are started.\n+ You want to specify an absolute pathname here.\n+ \n+ \n+\n+ \n+\n+ \n+\n+ \n+\n+
\n+ \n+ A section which allows a user to define arbitrary key-value pairs for\n+ use as environment variables during Zope\'s run cycle. It\n+ is not recommended to set system-related environment variables such as\n+ PYTHONPATH within this section.\n+ \n+
\n+\n+ \n+ \n+ The top-level directory which contains the "instance" data for the\n+ application server. It may also contain "etc", "bin", "log",\n+ and "var" directories depending on how you\'ve configured your Zope\n+ instance.\n+ \n+ \n+\n+ \n+ \n+ The directory used to store the default filestorage file used to\n+ back the ZODB database, as well as other files used by the\n+ Zope applications server during runtime.\n+ \n+ $instancehome/var\n+ \n+\n+ \n+ \n+ A switch which controls several aspects of Zope operation useful for\n+ developing under Zope. When debug mode is on:\n+\n+ - Errors in product initialization will cause startup to fail\n+ (instead of writing error messages to the event log file).\n+\n+ - Filesystem-based scripts such as skins, PageTemplateFiles, and\n+ DTMLFiles can be edited while the server is running and the server\n+ will detect these changes in real time. When this switch is\n+ off, you must restart the server to see the changes.\n+\n+ Setting this to \'off\' when Zope is in a production environment is\n+ encouraged, as it speeds execution (sometimes dramatically).\n+ \n+ off\n+ \n+\n+ \n+ \n+ Locale name to be used. See your operating system documentation for locale\n+ information specific to your system. If the requested locale is not\n+ supported by your system, an error will be raised and Zope will not start.\n+ \n+ unset\n+ \n+\n+ \n+ \n+ Set this variable either to "us" or "international" to force the\n+ DateTime module to parse date strings either with\n+ month-before-days-before-year ("us") or\n+ days-before-month-before-year ("international"). The default\n+ behaviour of DateTime (when this setting is left unset) is to\n+ parse dates as US dates.\n+ \n+ us\n+ \n+\n+ \n+ \n+ Value passed to Python\'s sys.setcheckinterval() function. The\n+ higher this is, the less frequently the Python interpreter\n+ checks for keyboard interrupts. Setting this to higher values\n+ also reduces the frequency of potential thread switches, which\n+ can improve the performance of a busy server.\n+ \n+ \n+\n+ \n+ \n+ The HTTP "Realm" header value sent by this Zope instance. This value\n+ often shows up in basic authentication dialogs.\n+ \n+ Zope\n+ \n+\n+ \n+ \n+ Set this directive to \'off\' in order to disable the autoquoting of\n+ implicitly retrieved REQUEST data by DTML code which contains a \'<\'\n+ when used in <dtml-var> construction. When this directive is \'on\',\n+ all data implicitly retrieved from the REQUEST in DTML (as opposed to\n+ addressing REQUEST.somevarname directly) that contains a \'<\' will be\n+ HTML-quoted when interpolated via a <dtml-var> or &dtml-\n+ construct. This mitigates the possibility that DTML programmers will\n+ leave their sites open to a "client-side trojan" attack.\n+ \n+ on\n+ \n+\n+ \n+ \n+ Define one or more \'trusted-proxies\' keys, each of which is a\n+ hostname or an IP address. The set of definitions comprises a list\n+ of front-end proxies that are trusted to supply an accurate\n+ X_FORWARDED_FOR header to Zope (security-related).\n+ \n+ unset\n+ \n+\n+ \n+ \n+ The maximum number of retries on a conflict error\n+ \n+ \n+\n+ \n+ \n+ The default Zope "security policy" implementation is written in C.\n+ Set this key to "PYTHON" to use the Python implementation\n+ (useful for debugging purposes); set it to "C" to use the C\n+ implementation.\n+ \n+ C\n+ \n+\n+ \n+ \n+ Set this directive to \'on\' to cause Zope to prevent Zope from\n+ attempting to authenticate users during normal operation.\n+ Potentially dangerous from a security perspective. Only works if\n+ security-policy-implementation is set to \'C\'.\n+ \n+ off\n+ \n+\n+ \n+ \n+ Set this directive to \'on\' to cause Zope to ignore ownership checking\n+ when attempting to execute "through the web" code. By default, this\n+ directive is off in order to prevent \'trojan horse\' security problems\n+ whereby a user with less privilege can cause a user with more\n+ privilege to execute code which the less privileged user has written.\n+ \n+ off\n+ \n+\n+ \n+ \n+ Set this directive to \'on\' to enable verbose security exceptions.\n+ This can help you track down the reason for Unauthorized exceptions,\n+ but it is not suitable for public sites because it may reveal\n+ unnecessary information about the structure of your site. Only\n+ works if security-policy-implementation is set to \'PYTHON\'.\n+ \n+ off\n+ \n+\n+
\n+ \n+ Describes what level of log output is desired and where it\n+ should be written.\n+ \n+
\n+\n+
\n+ \n+ Describes the logging performed to capture the \'access\' log,\n+ which typically captures per-request data in common or combined\n+ log format.\n+ \n+
\n+\n+
\n+ \n+ Describes the logging performed to capture the \'trace\' log,\n+ which typically captures detailed per-request data useful for\n+ Zope debugging.\n+ \n+
\n+\n+ \n+ \n+ Specifies at which level conflict errors are logged. Conflict\n+ errors, when occurring in small numbers, are a normal part of the\n+ Zope optimistic transaction conflict resolution algorithms. They\n+ are retried automatically a few times, and are therefore usually\n+ not visible by the user. You can specify \'notset\' if you don\'t\n+ want them logged, or use any other logger level.\n+ \n+ info\n+ \n+\n+ \n+ \n+ Zope ZODB databases must have a name, and they are required to be\n+ referenced via the "zodb_db" database type because it is\n+ the only kind of database definition that implements\n+ the required mount-point argument. There is another\n+ database sectiontype named "zodb", but it cannot be used\n+ in the context of a proper Zope configuration (due to\n+ lack of a mount-point).\n+ \n+ \n+\n+ \n+ \n+ This key controls what character set is used to encode unicode\n+ data that reaches ZPublisher without any other specified encoding.\n+ \n+ \n+\n+ \n+ \n+ Base type for product-specific configuration sections.\n+\n+ Specific products should implement configuration sections by\n+ defining section types that implement this abstract type and\n+ using their own schema component to define meaningful settings.\n+\n+ \n+ \n+\n+ \n+ \n+ Product-specific configuration, expressed as arbitrary name-value pairs.\n+ \n+\n+ \n+ \n+\n+ \n+ \n+ Product-specific configuration stanzas.\n+\n+ Products may use the <product-config> section type, or may supply\n+ a component.xml which defines section types with their own schemas.\n+\n+ All sections for this multisection will be collected into the\n+ \'product_config\' attribute of the configuration object.\n+ \n+ \n+\n+
\n+\n+ \n+ \n+ Path to the Python interpreter for use by zdaemon.\n+ Defaults to sys.executable.\n+ Needed for buildout-based instances to supply a python\n+ that has all the correct eggs on the path.\n+ \n+ \n+\n+\ndiff --git a/src/plone/recipe/zope2instance/zopectl.py b/src/plone/recipe/zope2instance/zopectl.py\nnew file mode 100644\nindex 0000000..5610d0b\n--- /dev/null\n+++ b/src/plone/recipe/zope2instance/zopectl.py\n@@ -0,0 +1,430 @@\n+##############################################################################\n+#\n+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.\n+# All Rights Reserved.\n+#\n+# This software is subject to the provisions of the Zope Public License,\n+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.\n+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED\n+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS\n+# FOR A PARTICULAR PURPOSE.\n+#\n+##############################################################################\n+"""zopectl -- control Zope using zdaemon.\n+\n+Usage: zopectl [options] [action [arguments]]\n+\n+Options:\n+-h/--help -- print usage message and exit\n+-b/--backoff-limit SECONDS -- set backoff limit to SECONDS (default 10)\n+-d/--daemon -- run as a proper daemon; fork a subprocess, close files etc.\n+-f/--forever -- run forever (by default, exit when backoff limit is exceeded)\n+-h/--help -- print this usage message and exit\n+-i/--interactive -- start an interactive shell after executing commands\n+-l/--logfile -- log file to be read by logtail command\n+-u/--user -- run the daemon manager program as this user (or numeric id)\n+-m/--umask -- provide octal umask for files created by the managed process\n+-s/--socket-name -- socket between zopectl and zdrun\n+-t/--transcript FILE -- log file where to redirect stdout and stderr\n+action [arguments] -- see below\n+\n+Actions are commands like "start", "stop" and "status". If -i is\n+specified or no action is specified on the command line, a "shell"\n+interpreting actions typed interactively is started. Use the\n+action "help" to find out about available actions.\n+"""\n+from __future__ import absolute_import\n+\n+import csv\n+import os\n+import sys\n+import signal\n+import xml.sax\n+\n+import pkg_resources\n+\n+import zdaemon\n+from zdaemon.zdctl import ZDCmd, ZDCtlOptions\n+from zdaemon.zdoptions import ZDOptions\n+from ZConfig.components.logger.handlers import FileHandlerFactory\n+from ZConfig.components.logger import logger\n+from ZConfig.loader import SchemaLoader\n+from Zope2.Startup.options import ConditionalSchemaParser\n+\n+try:\n+ from ZServer.Zope2.Startup.options import ZopeOptions\n+except ImportError:\n+ class ZopeOptions(ZDOptions):\n+ schemadir = os.path.dirname(os.path.abspath(__file__))\n+ schemafile = \'wsgischema.xml\'\n+\n+\n+if sys.version_info > (3, ):\n+ basestring = str\n+\n+WIN = False\n+if sys.platform[:3].lower() == "win":\n+ WIN = True\n+\n+\n+def string_list(arg):\n+ return arg.split()\n+\n+\n+def quote_command(command):\n+ print(" ".join(command))\n+ # Quote the program name, so it works even if it contains spaces\n+ command = " ".join([\'"%s"\' % x for x in command])\n+ if WIN:\n+ # odd, but true: the windows cmd processor can\'t handle more than\n+ # one quoted item per string unless you add quotes around the\n+ # whole line.\n+ command = \'"%s"\' % command\n+ return command\n+\n+\n+class LoggerFactory(logger.LoggerFactory):\n+ """\n+ A factory used to create loggers while delaying actual logger\n+ instance construction. We need to do this because we may want to\n+ reference a logger before actually instantiating it (for example,\n+ to allow the app time to set an effective user). An instance of\n+ this wrapper is a callable which, when called, returns a logger\n+ object.\n+ """\n+ def __init__(self, section):\n+ section.name = section.getSectionName()\n+ section.propagate = False\n+ logger.LoggerFactory.__init__(self, section)\n+\n+\n+class ZopeCtlOptions(ZopeOptions, ZDCtlOptions):\n+ # Zope controller options.\n+ #\n+ # After initialization, this should look very much like a\n+ # zdaemon.zdctl.ZDCtlOptions instance. Many of the attributes are\n+ # initialized from different sources, however.\n+\n+ # Provide help message, without indentation.\n+ __doc__ = __doc__\n+\n+ positional_args_allowed = True\n+\n+ # this indicates that no explicit program has been provided.\n+ # the command line option can set this.\n+ program = None\n+\n+ # this indicates that no explicit socket name has been provided.\n+ # the command line option can set this.\n+ sockname = None\n+\n+ # XXX Suppress using Zope\'s section to avoid using the\n+ # same logging for zdctl as for the Zope appserver. There still\n+ # needs to be a way to set a logfile for zdctl.\n+ logsectionname = None\n+\n+ def __init__(self):\n+ ZopeOptions.__init__(self)\n+ ZDCtlOptions.__init__(self)\n+ self.add("interactive", None, "i", "interactive", flag=1)\n+ self.add("default_to_interactive", "runner.default_to_interactive",\n+ default=1)\n+\n+ def realize(self, *args, **kw):\n+ ZopeOptions.realize(self, *args, **kw)\n+ # Additional checking of user option; set uid and gid\n+ if self.user is not None:\n+ import pwd\n+ try:\n+ uid = int(self.user)\n+ except ValueError:\n+ try:\n+ pwrec = pwd.getpwnam(self.user)\n+ except KeyError:\n+ self.usage("username %r not found" % self.user)\n+ uid = pwrec[2]\n+ else:\n+ try:\n+ pwrec = pwd.getpwuid(uid)\n+ except KeyError:\n+ self.usage("uid %r not found" % self.user)\n+ gid = pwrec[3]\n+ self.uid = uid\n+ self.gid = gid\n+\n+ config = self.configroot\n+ self.directory = config.instancehome\n+ self.clienthome = config.clienthome\n+ if self.program:\n+ if isinstance(self.program, basestring):\n+ self.program = [self.program]\n+ elif config.runner and config.runner.program:\n+ self.program = config.runner.program\n+ else:\n+ self.program = [os.path.join(self.directory, "bin", "runzope")]\n+ if self.sockname:\n+ # set by command line option\n+ pass\n+ elif config.runner and config.runner.socket_name:\n+ self.sockname = config.runner.socket_name\n+ else:\n+ self.sockname = os.path.join(self.clienthome, "zopectlsock")\n+ self.python = os.environ.get(\'PYTHON\', config.python) or sys.executable\n+ self.zdrun = os.path.join(os.path.dirname(zdaemon.__file__),\n+ "zdrun.py")\n+\n+ self.exitcodes = [0, 2]\n+ if self.logfile is None and config.eventlog is not None:\n+ for handler in config.eventlog.handler_factories:\n+ if isinstance(handler, FileHandlerFactory):\n+ self.logfile = handler.section.path\n+ if self.logfile not in ("STDERR", "STDOUT"):\n+ break\n+\n+ def load_schema(self):\n+ if self.schema is None:\n+ # Load schema\n+ if self.schemadir is None:\n+ self.schemadir = os.path.dirname(__file__)\n+ self.schemafile = os.path.join(self.schemadir, self.schemafile)\n+ self._conditional_load()\n+\n+ def _conditional_load(self):\n+ loader = SchemaLoader()\n+ # loadURL\n+ url = loader.normalizeURL(self.schemafile)\n+ resource = loader.openResource(url)\n+ try:\n+ # load / parseResource without caching\n+ parser = ConditionalSchemaParser(loader, resource.url)\n+ xml.sax.parse(resource.file, parser)\n+ self.schema = parser._schema\n+ finally:\n+ resource.close()\n+\n+\n+class ZopeCmd(ZDCmd):\n+\n+ _exitstatus = 0\n+\n+ def _get_override(self, opt, name, svalue=None, flag=0):\n+ # Suppress the config file, and pass all configuration via the\n+ # command line. This avoids needing to specialize the zdrun\n+ # script.\n+ if name == "configfile":\n+ return []\n+ value = getattr(self.options, name)\n+ if value is None:\n+ return []\n+ if flag:\n+ if value:\n+ args = [opt]\n+ else:\n+ args = []\n+ else:\n+ if svalue is None:\n+ svalue = str(value)\n+ args = [opt, svalue]\n+ return args\n+\n+ def get_startup_cmd(self, python, more):\n+ cmdline = (\'%s -c "from Zope2 import configure;\'\n+ \'configure(%r);\' %\n+ (python, self.options.configfile)\n+ )\n+ return cmdline + more + \'\\"\'\n+\n+ def do_debug(self, arg):\n+ cmdline = self.get_startup_cmd(self.options.python + \' -i\',\n+ \'import Zope2; app=Zope2.app()\')\n+ print(\'Starting debugger (the name "app" is bound to the top-level \'\n+ \'Zope object)\')\n+ os.system(cmdline)\n+\n+ def do_foreground(self, arg):\n+ program = self.options.program\n+ local_additions = []\n+ if not program.count(\'-C\'):\n+ local_additions += [\'-C\', self.options.configfile]\n+ if not program.count(\'-X\'):\n+ local_additions += [\'-X\']\n+ if not program.count(\'debug-mode=on\'):\n+ local_additions += [\'debug-mode=on\']\n+ program[1:1] = local_additions\n+ command = quote_command(program)\n+ try:\n+ return os.system(command)\n+ except KeyboardInterrupt:\n+ pass\n+ finally:\n+ for addition in local_additions:\n+ program.remove(addition)\n+\n+ def help_debug(self):\n+ print("debug -- run the Zope debugger to inspect your database")\n+ print(" manually using a Python interactive shell")\n+\n+ def __getattr__(self, name):\n+ """\n+ Getter to check if an unknown command is implement by an entry point.\n+ """\n+ if not name.startswith("do_"):\n+ raise AttributeError(name)\n+ data = list(pkg_resources.iter_entry_points(\n+ "zopectl.command", name=name[3:]))\n+ if not data:\n+ raise AttributeError(name)\n+ if len(data) > 1:\n+ sys.stderr.write(\n+ "Warning: multiple entry points found for command")\n+ return\n+ func = data[0].load()\n+ if not callable(func):\n+ sys.stderr.write("Error: %s is not a callable method" % name)\n+ return\n+\n+ return self.run_entrypoint(data[0])\n+\n+ def run_entrypoint(self, entry_point):\n+ def go(arg):\n+ # If the command line was something like\n+ # """bin/instance run "one two" three"""\n+ # cmd.parseline will have converted it so\n+ # that arg == \'one two three\'. This is going to\n+ # foul up any quoted command with embedded spaces.\n+ # So we have to return to self.options.args,\n+ # which is a tuple of command line args,\n+ # throwing away the "run" command at the beginning.\n+ #\n+ # Further complications: if self.options.args has come\n+ # via subprocess, it may look like\n+ # [\'run "arg 1" "arg2"\'] rather than [\'run\',\'arg 1\',\'arg2\'].\n+ # If that\'s the case, we\'ll use csv to do the parsing\n+ # so that we can split on spaces while respecting quotes.\n+ tup = self.options.args\n+ if len(tup) == 1:\n+ tup = csv.reader(tup, delimiter=\' \').next()\n+\n+ # Remove -c and add command name as sys.argv[0]\n+ cmd = [\'import sys\',\n+ \'sys.argv.pop()\',\n+ \'sys.argv.append(r\\\'%s\\\')\' % entry_point.name\n+ ]\n+ if len(tup) > 1:\n+ argv = tup[1:]\n+ for a in argv:\n+ cmd.append(\'sys.argv.append(r\\\'%s\\\')\' % a)\n+ cmd.extend([\n+ \'import pkg_resources\',\n+ \'import Zope2\',\n+ \'func=pkg_resources.EntryPoint.parse(\\\'%s\\\').load(False)\'\n+ % entry_point,\n+ \'app=Zope2.app()\',\n+ \'func(app, sys.argv[1:])\',\n+ ])\n+ cmdline = self.get_startup_cmd(\n+ self.options.python, \' ; \'.join(cmd))\n+ self._exitstatus = os.system(cmdline)\n+ return go\n+\n+ def do_run(self, args):\n+ if not args:\n+ print("usage: run