Updated .Net library to .netstandard2.0

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SharpKeeperRecipes.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("SharpKeeperRecipes.Tests")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a1b5ade2-64f4-46ee-b8eb-067c351afb45")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ZooKeeperNet.Recipes")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2fa24a05-e792-49c5-908a-9ae26ecb6401")]

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
namespace ZooKeeperNet.Tests
{
    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using log4net.Config;

    public abstract class AbstractZooKeeperTests
    {
        static AbstractZooKeeperTests()
        {
            XmlConfigurator.Configure();
        }

        protected static readonly TimeSpan CONNECTION_TIMEOUT = new TimeSpan(0, 0, 0, 0, 10000);

        protected virtual ZooKeeper CreateClient()
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper("127.0.0.1:2181", new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected virtual ZooKeeper CreateClient(string node)
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper("127.0.0.1:2181" + node, new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected ZooKeeper CreateClient(IWatcher watcher)
        {
            return new ZooKeeper("127.0.0.1:2181", new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected ZooKeeper CreateClientWithAddress(string address)
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper(address, new TimeSpan(0, 0, 0, 10000), watcher);
        }

        public class CountdownWatcher : IWatcher
        {
            readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
            private static readonly object sync = new object();

            volatile bool connected;

            public CountdownWatcher()
            {
                Reset();
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public void Reset()
            {
                resetEvent.Set();
                connected = false;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public virtual void Process(WatchedEvent @event)
            {
                if (@event.State == KeeperState.SyncConnected)
                {
                    connected = true;
                    lock (sync)
                    {
                        Monitor.PulseAll(sync);
                    }
                    resetEvent.Set();
                }
                else
                {
                    connected = false;
                    lock (sync)
                    {
                        Monitor.PulseAll(sync);
                    }
                }
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            bool IsConnected()
            {
                return connected;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            void waitForConnected(TimeSpan timeout)
            {
                DateTime expire = DateTime.UtcNow + timeout;
                TimeSpan left = timeout;
                while (!connected && left.TotalMilliseconds > 0)
                {
                    lock (sync)
                    {
                        Monitor.TryEnter(sync, left);
                    }
                    left = expire - DateTime.UtcNow;
                }
                if (!connected)
                {
                    throw new TimeoutException("Did not connect");

                }
            }

            void waitForDisconnected(TimeSpan timeout)
            {
                DateTime expire = DateTime.UtcNow + timeout;
                TimeSpan left = timeout;
                while (connected && left.TotalMilliseconds > 0)
                {
                    lock (sync)
                    {
                        Monitor.TryEnter(sync, left);
                    }
                    left = expire - DateTime.UtcNow;
                }
                if (connected)
                {
                    throw new TimeoutException("Did not disconnect");
                }
            }
        }

    }
}

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SharpKeeper.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("SharpKeeper.Tests")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0755fc77-447a-4817-b43f-82ba61d1c2a6")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Threading;
using log4net;
using NUnit.Framework;
using Org.Apache.Zookeeper.Data;

namespace ZooKeeperNet.Tests
{
    public class RecoveryTest : AbstractZooKeeperTests, IWatcher
    {
        private readonly ILog LOG = LogManager.GetLogger(typeof(RecoveryTest));
        private int setDataCount;
        private int processCount;
        private readonly string testPath = "/unittests/recoverytests/" + Guid.NewGuid();

        [Test, Explicit]
        public void ReconnectsWhenDisconnected()
        {
            using (CancellationTokenSource token = new CancellationTokenSource())
            {
                Thread thread = new Thread(Run)
                    {
                        IsBackground = true,
                        Name = "RecoveryTest.Run"
                    };
                thread.Start(token.Token);
                Thread.Sleep(15*1000);
                LOG.Error("STOP ZK!!!! Count: " + processCount);
                Thread.Sleep(20*1000);
                LOG.Error("START ZK!!! Count: " + processCount);
                Thread.Sleep(30*1000);
                LOG.Error("Stopping ZK client.");
                token.Cancel();
                LOG.Error("Waiting for thread to stop..." + processCount);
                thread.Join();
                if (thread.IsAlive)
                    Assert.Fail("Thread still alive");
                Assert.AreEqual(setDataCount, processCount, "setDataCount == processCount");
                LOG.Error("Finished:" + setDataCount + ":" + processCount);
            }
        }

        private void Run(object sender)
        {
            try
            {
                CancellationToken token = (CancellationToken)sender;
                using (ZooKeeper zooKeeper = CreateClient(this))
                {
                    Stat stat = new Stat();
                    if (zooKeeper.Exists("/unittests/recoverytests", false) == null)
                    {
                        zooKeeper.Create("/unittests", new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                        zooKeeper.Create("/unittests/recoverytests", new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                    }
                    if (zooKeeper.Exists(testPath, false) == null)
                    {
                        zooKeeper.Create(testPath, new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                    }
                    while (zooKeeper.State.IsAlive() && !token.IsCancellationRequested)
                    {
                        try
                        {
                            zooKeeper.GetData(testPath, true, stat);
                            zooKeeper.SetData(testPath, Guid.NewGuid().ToByteArray(), -1);
                            setDataCount++;
                        }
                        catch(KeeperException ke)
                        {
                            LOG.Error(ke);
                        }
                    }
                    LOG.Error("Waiting for dispose.");
                }
            }
            catch(Exception ex)
            {
                LOG.Error(ex);
            }
        }

        public void Process(WatchedEvent @event)
        {
            LOG.Debug(@event);
            if (@event.Type == EventType.NodeCreated || @event.Type == EventType.NodeDataChanged)
            {
                Interlocked.Increment(ref processCount);
            }
        }
    }
}

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ZooKeeperNet")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0ee34fd6-195e-4912-906a-44062ecaffad")]

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

using System.IO;
using System.Xml;

namespace ZooKeeperNet.Tests
{
    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;

    public abstract class AbstractZooKeeperTests
    {
        static AbstractZooKeeperTests()
        {
            XmlDocument log4netConfig = new XmlDocument();
            log4netConfig.Load(File.OpenRead("log4net.xml"));
            var repository = log4net.LogManager.CreateRepository("tests");
            log4net.Config.XmlConfigurator.Configure(repository, log4netConfig["log4net"]);
        }

        protected static readonly TimeSpan CONNECTION_TIMEOUT = new TimeSpan(0, 0, 0, 0, 10000);

        protected virtual ZooKeeper CreateClient()
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper("127.0.0.1:2181", new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected virtual ZooKeeper CreateClient(string node)
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper("127.0.0.1:2181" + node, new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected ZooKeeper CreateClient(IWatcher watcher)
        {
            return new ZooKeeper("127.0.0.1:2181", new TimeSpan(0, 0, 0, 10000), watcher);
        }

        protected ZooKeeper CreateClientWithAddress(string address)
        {
            CountdownWatcher watcher = new CountdownWatcher();
            return new ZooKeeper(address, new TimeSpan(0, 0, 0, 10000), watcher);
        }

        public class CountdownWatcher : IWatcher
        {
            readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
            private static readonly object sync = new object();

            volatile bool connected;

            public CountdownWatcher()
            {
                Reset();
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public void Reset()
            {
                resetEvent.Set();
                connected = false;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            public virtual void Process(WatchedEvent @event)
            {
                if (@event.State == KeeperState.SyncConnected)
                {
                    connected = true;
                    lock (sync)
                    {
                        Monitor.PulseAll(sync);
                    }
                    resetEvent.Set();
                }
                else
                {
                    connected = false;
                    lock (sync)
                    {
                        Monitor.PulseAll(sync);
                    }
                }
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            bool IsConnected()
            {
                return connected;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            void waitForConnected(TimeSpan timeout)
            {
                DateTime expire = DateTime.UtcNow + timeout;
                TimeSpan left = timeout;
                while (!connected && left.TotalMilliseconds > 0)
                {
                    lock (sync)
                    {
                        Monitor.TryEnter(sync, left);
                    }
                    left = expire - DateTime.UtcNow;
                }
                if (!connected)
                {
                    throw new TimeoutException("Did not connect");

                }
            }

            void waitForDisconnected(TimeSpan timeout)
            {
                DateTime expire = DateTime.UtcNow + timeout;
                TimeSpan left = timeout;
                while (connected && left.TotalMilliseconds > 0)
                {
                    lock (sync)
                    {
                        Monitor.TryEnter(sync, left);
                    }
                    left = expire - DateTime.UtcNow;
                }
                if (connected)
                {
                    throw new TimeoutException("Did not disconnect");
                }
            }
        }

    }
}

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Threading;
using log4net;
using NUnit.Framework;
using Org.Apache.Zookeeper.Data;

namespace ZooKeeperNet.Tests
{
    public class RecoveryTest : AbstractZooKeeperTests, IWatcher
    {
        private readonly ILog LOG = LogManager.GetLogger(typeof(RecoveryTest));
        private int setDataCount;
        private int processCount;
        private readonly string testPath = "/unittests/recoverytests/" + Guid.NewGuid();

        [Test, Explicit]
        public void ReconnectsWhenDisconnected()
        {
            using (
                CancellationTokenSource token = new CancellationTokenSource())
            {
                Thread thread = new Thread(Run)
                    {
                        IsBackground = true,
                        Name = "RecoveryTest.Run"
                    };
                thread.Start(token.Token);
                Thread.Sleep(15*1000);
                LOG.Error("STOP ZK!!!! Count: " + processCount);
                Thread.Sleep(20*1000);
                LOG.Error("START ZK!!! Count: " + processCount);
                Thread.Sleep(30*1000);
                LOG.Error("Stopping ZK client.");
                token.Cancel();
                LOG.Error("Waiting for thread to stop..." + processCount);
                thread.Join();
                if (thread.IsAlive)
                    Assert.Fail("Thread still alive");
                Assert.AreEqual(setDataCount, processCount, "setDataCount == processCount");
                LOG.Error("Finished:" + setDataCount + ":" + processCount);
            }
        }

        private void Run(object sender)
        {
            try
            {
                CancellationToken token = (CancellationToken)sender;
                using (ZooKeeper zooKeeper = CreateClient(this))
                {
                    Stat stat = new Stat();
                    if (zooKeeper.Exists("/unittests/recoverytests", false) == null)
                    {
                        zooKeeper.Create("/unittests", new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                        zooKeeper.Create("/unittests/recoverytests", new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                    }
                    if (zooKeeper.Exists(testPath, false) == null)
                    {
                        zooKeeper.Create(testPath, new byte[] {0}, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                    }
                    while (zooKeeper.State.IsAlive() && !token.IsCancellationRequested)
                    {
                        try
                        {
                            zooKeeper.GetData(testPath, true, stat);
                            zooKeeper.SetData(testPath, Guid.NewGuid().ToByteArray(), -1);
                            setDataCount++;
                        }
                        catch(KeeperException ke)
                        {
                            LOG.Error(ke);
                        }
                    }
                    LOG.Error("Waiting for dispose.");
                }
            }
            catch(Exception ex)
            {
                LOG.Error(ex);
            }
        }

        public void Process(WatchedEvent @event)
        {
            LOG.Debug(@event);
            if (@event.Type == EventType.NodeCreated || @event.Type == EventType.NodeDataChanged)
            {
                Interlocked.Increment(ref processCount);
            }
        }
    }
}


Dev

"""
    "django-tools
    miscellaneous tools for Django based projects
"""

__version__ = '0.56.0'
__author__ = 'Jens Diemer <django-tools@jensdiemer.de>'

import os
import unittest.util
from pathlib import Path

from bx_py_utils.test_utils.deny_requests import deny_any_real_request

def pre_configure_tests() -> None:
    print(f'Configure unittests via "load_tests Protocol" from {Path(__file__).relative_to(Path.cwd())}')

    # Hacky way to display more "assert"-Context in failing tests:
    _MIN_MAX_DIFF = unittest.util._MAX_LENGTH - unittest.util._MIN_DIFF_LEN
    unittest.util._MAX_LENGTH = int(os.environ.get('UNITTEST_MAX_LENGTH', 300))
    unittest.util._MIN_DIFF_LEN = unittest.util._MAX_LENGTH - _MIN_MAX_DIFF

    # Deny any request via docket/urllib3 because tests they should mock all requests:
    deny_any_real_request()

def load_tests(loader, tests, pattern):
    """
    Use unittest "load_tests Protocol" as a hook to setup test environment before running tests.
    https://docs.python.org/3/library/unittest.html#load-tests-protocol
    """
    pre_configure_tests()
    return loader.discover(start_dir=Path(__file__).parent, pattern=pattern)

#!/usr/bin/env python3

"""
    bootstrap CLI
    ~~~~~~~~~~~~~

    Just call this file, and the magic happens ;)
"""

import hashlib
import shlex
import signal
import subprocess
import sys
import venv
from pathlib import Path

def print_no_pip_error():
    print('Error: Pip not available!')
    print('Hint: "apt-get install python3-venv"\n')

try:
    from ensurepip import version
except ModuleNotFoundError as err:
    print(err)
    print('-' * 100)
    print_no_pip_error()
    raise
else:
    if not version():
        print_no_pip_error()
        sys.exit(-1)

assert sys.version_info >= (3, 10), f'Python version {sys.version_info} is too old!'

if sys.platform == 'win32':  # wtf
    # Files under Windows, e.g.: .../.venv/Scripts/python.exe
    BIN_NAME = 'Scripts'
    FILE_EXT = '.exe'
else:
    # Files under Linux/Mac and all other than Windows, e.g.: .../.venv/bin/python
    BIN_NAME = 'bin'
    FILE_EXT = ''

BASE_PATH = Path(__file__).parent
VENV_PATH = BASE_PATH / '.venv'
BIN_PATH = VENV_PATH / BIN_NAME
PYTHON_PATH = BIN_PATH / f'python{FILE_EXT}'
PIP_PATH = BIN_PATH / f'pip{FILE_EXT}'
PIP_SYNC_PATH = BIN_PATH / f'pip-sync{FILE_EXT}'

DEP_LOCK_PATH = BASE_PATH / 'requirements.dev.txt'
DEP_HASH_PATH = VENV_PATH / '.dep_hash'

# script file defined in pyproject.toml as [console_scripts]
# (Under Windows: ".exe" not added!)
PROJECT_SHELL_SCRIPT = BIN_PATH / 'django_tools_project'

def get_dep_hash():
    """Get SHA512 hash from lock file content."""
    return hashlib.sha512(DEP_LOCK_PATH.read_bytes()).hexdigest()

def store_dep_hash():
    """Generate .venv/.dep_hash"""
    DEP_HASH_PATH.write_text(get_dep_hash())

def venv_up2date():
    """Is existing .venv is up-to-date?"""
    if DEP_HASH_PATH.is_file():
        return DEP_HASH_PATH.read_text() == get_dep_hash()
    return False

def verbose_check_call(*popen_args):
    print(f'\n+ {shlex.join(str(arg) for arg in popen_args)}\n')
    return subprocess.check_call(popen_args)

def noop_sigint_handler(signal_num, frame):
    """
    Don't exist cmd2 shell on "Interrupt from keyboard"
    e.g.: User stops the dev. server by CONTROL-C
    """

def main(argv):
    assert DEP_LOCK_PATH.is_file(), f'File not found: "{DEP_LOCK_PATH}" !'

    # Create virtual env in ".venv/":
    if not PYTHON_PATH.is_file():
        print(f'Create virtual env here: {VENV_PATH.absolute()}')
        builder = venv.EnvBuilder(symlinks=True, upgrade=True, with_pip=True)
        builder.create(env_dir=VENV_PATH)

    if not PROJECT_SHELL_SCRIPT.is_file() or not venv_up2date():
        # Update pip
        verbose_check_call(PYTHON_PATH, '-m', 'pip', 'install', '-U', 'pip')

        # Install pip-tools
        verbose_check_call(PYTHON_PATH, '-m', 'pip', 'install', '-U', 'pip-tools')

        # install requirements via "pip-sync"
        verbose_check_call(PIP_SYNC_PATH, str(DEP_LOCK_PATH))

        # install project
        verbose_check_call(PIP_PATH, 'install', '--no-deps', '-e', '.')
        store_dep_hash()

    signal.signal(signal.SIGINT, noop_sigint_handler)  # ignore "Interrupt from keyboard" signals

    # Call our entry point CLI:
    try:
        verbose_check_call(PROJECT_SHELL_SCRIPT, *argv[1:])
    except subprocess.CalledProcessError as err:
        sys.exit(err.returncode)

if __name__ == '__main__':
    main(sys.argv)

"""
    "django-tools
    miscellaneous tools for Django based projects
"""

__version__ = '0.56.1'
__author__ = 'Jens Diemer <django-tools@jensdiemer.de>'

import os
import unittest.util
from pathlib import Path

from bx_py_utils.test_utils.deny_requests import deny_any_real_request
from typeguard import install_import_hook

# Check type annotations via typeguard in all tests:
install_import_hook(packages=('django_tools', 'django_tools_project'))

def pre_configure_tests() -> None:
    print(f'Configure unittests via "load_tests Protocol" from {Path(__file__).relative_to(Path.cwd())}')

    # Hacky way to display more "assert"-Context in failing tests:
    _MIN_MAX_DIFF = unittest.util._MAX_LENGTH - unittest.util._MIN_DIFF_LEN
    unittest.util._MAX_LENGTH = int(os.environ.get('UNITTEST_MAX_LENGTH', 600))
    unittest.util._MIN_DIFF_LEN = unittest.util._MAX_LENGTH - _MIN_MAX_DIFF

    # Deny any request via docket/urllib3 because tests they should mock all requests:
    deny_any_real_request()

def load_tests(loader, tests, pattern):
    """
    Use unittest "load_tests Protocol" as a hook to setup test environment before running tests.
    https://docs.python.org/3/library/unittest.html#load-tests-protocol
    """
    pre_configure_tests()
    return loader.discover(start_dir=Path(__file__).parent, pattern=pattern)

#!/usr/bin/env python3

"""
    bootstrap CLI
    ~~~~~~~~~~~~~

    Just call this file, and the magic happens ;)
"""

import hashlib
import shlex
import signal
import subprocess
import sys
import venv
from pathlib import Path

def print_no_pip_error():
    print('Error: Pip not available!')
    print('Hint: "apt-get install python3-venv"\n')

try:
    from ensurepip import version
except ModuleNotFoundError as err:
    print(err)
    print('-' * 100)
    print_no_pip_error()
    raise
else:
    if not version():
        print_no_pip_error()
        sys.exit(-1)

assert sys.version_info >= (3, 10), f'Python version {sys.version_info} is too old!'

if sys.platform == 'win32':  # wtf
    # Files under Windows, e.g.: .../.venv/Scripts/python.exe
    BIN_NAME = 'Scripts'
    FILE_EXT = '.exe'
else:
    # Files under Linux/Mac and all other than Windows, e.g.: .../.venv/bin/python
    BIN_NAME = 'bin'
    FILE_EXT = ''

BASE_PATH = Path(__file__).parent
VENV_PATH = BASE_PATH / '.venv'
BIN_PATH = VENV_PATH / BIN_NAME
PYTHON_PATH = BIN_PATH / f'python3{FILE_EXT}'
PIP_PATH = BIN_PATH / f'pip{FILE_EXT}'
PIP_SYNC_PATH = BIN_PATH / f'pip-sync{FILE_EXT}'

DEP_LOCK_PATH = BASE_PATH / 'requirements.dev.txt'
DEP_HASH_PATH = VENV_PATH / '.dep_hash'

# script file defined in pyproject.toml as [console_scripts]
# (Under Windows: ".exe" not added!)
PROJECT_SHELL_SCRIPT = BIN_PATH / 'django_tools_project'

def get_dep_hash():
    """Get SHA512 hash from lock file content."""
    return hashlib.sha512(DEP_LOCK_PATH.read_bytes()).hexdigest()

def store_dep_hash():
    """Generate .venv/.dep_hash"""
    DEP_HASH_PATH.write_text(get_dep_hash())

def venv_up2date():
    """Is existing .venv is up-to-date?"""
    if DEP_HASH_PATH.is_file():
        return DEP_HASH_PATH.read_text() == get_dep_hash()
    return False

def verbose_check_call(*popen_args):
    print(f'\n+ {shlex.join(str(arg) for arg in popen_args)}\n')
    return subprocess.check_call(popen_args)

def noop_sigint_handler(signal_num, frame):
    """
    Don't exist cmd2 shell on "Interrupt from keyboard"
    e.g.: User stops the dev. server by CONTROL-C
    """

def main(argv):
    assert DEP_LOCK_PATH.is_file(), f'File not found: "{DEP_LOCK_PATH}" !'

    # Create virtual env in ".venv/":
    if not PYTHON_PATH.is_file():
        print(f'Create virtual env here: {VENV_PATH.absolute()}')
        builder = venv.EnvBuilder(symlinks=True, upgrade=True, with_pip=True)
        builder.create(env_dir=VENV_PATH)

    if not PROJECT_SHELL_SCRIPT.is_file() or not venv_up2date():
        # Update pip
        verbose_check_call(PYTHON_PATH, '-m', 'pip', 'install', '-U', 'pip')

        # Install pip-tools
        verbose_check_call(PYTHON_PATH, '-m', 'pip', 'install', '-U', 'pip-tools')

        # install requirements via "pip-sync"
        verbose_check_call(PIP_SYNC_PATH, str(DEP_LOCK_PATH))

        # install project
        verbose_check_call(PIP_PATH, 'install', '--no-deps', '-e', '.')
        store_dep_hash()

        # Activate git pre-commit hooks:
        verbose_check_call(PYTHON_PATH, '-m', 'pre_commit', 'install')
        verbose_check_call(PYTHON_PATH, '-m', 'pre_commit', 'autoupdate')

    signal.signal(signal.SIGINT, noop_sigint_handler)  # ignore "Interrupt from keyboard" signals

    # Call our entry point CLI:
    try:
        verbose_check_call(PROJECT_SHELL_SCRIPT, *argv[1:])
    except subprocess.CalledProcessError as err:
        sys.exit(err.returncode)

if __name__ == '__main__':
    main(sys.argv)


Modificadiones solicitadas


hmason

#!/usr/bin/env python
# encoding: utf-8
# recognize.
"""
hackmatch.py

Created by Hilary Mason, Chris Wiggins, and Evan Korth.
Copyright (c) 2010 hackNY. All rights reserved.
"""

import sys, os
import csv
import string
from collections import defaultdict
from optparse import OptionParser
from nltk.tokenize import *
from nltk.corpus import stopwords
from hcluster import jaccard

# startups: Name,E-mail,Company,In NYC,Funding,Site,Blog,Twitter,Num Employees,Environment,Project,Skills,Misc
# students: Student Name,e-mail,University,Major,Degree,Graduation Date,Site,Blog,Twitter,Facebook,Project,Skills,Misc

class HackMatch(object):
    DEBUG = False
    BOW_FIELDS = ['Environment', 'Project', 'Skills', 'Misc']
    COMPLETENESS_THRESHOLD = 4 # num of words necessary to match

    def __init__(self, student_file, startup_file, num_matches=3, distance=jaccard):
        self.stopwords = self.get_stopwords()
        self.distance = distance

        student_data = self.parseCSV(student_file)
        startup_data = self.parseCSV(startup_file)

        doc_words = self.defineFeatures([student_data, startup_data], self.BOW_FIELDS)

        # matches = self.doRanking(student_data, startup_data, doc_words, self.BOW_FIELDS, base_name_field='Student Name', match_name_field='Company')
        matches = self.doRanking(startup_data, student_data, doc_words, self.BOW_FIELDS)

        self.printMatches(matches, num_matches)

    def printMatches(self, matches, num_matches):
        for n, m in matches.items():
            print n
            for item, score in sorted(m.items(), key=lambda(i,c):(-c, i))[:num_matches]:
                print "\t%s :: %s" % (item, score)
                # print "'%s' '%s' %s" % (n.translate(string.maketrans("",""), string.punctuation), item.translate(string.maketrans("",""), string.punctuation), score)
            print '\n'

    def doRanking(self, base_data, match_data, doc_words, fields=[], base_name_field='Company', match_name_field='Student Name'):
        """
        do ranking
        """
        base = {}
        for item in base_data:
            base[item[base_name_field]] = self.extractFeatures(item, doc_words, fields)

        matches = defaultdict(dict)
        for match_item in match_data:
            match_features = self.extractFeatures(match_item, doc_words, fields)

            for base_item, base_item_features in base.items(): # actually do the comparison
                if not base_item_features or not match_features:
                    matches[match_item[match_name_field]][base_item] = 0.0
                else:
                    matches[match_item[match_name_field]][base_item] = self.distance(base_item_features, match_features)
                if self.DEBUG:
                    print "%s :: %s = %s " % (match_item[match_name_field], base_item, self.distance(base_item_features, match_features))

        return matches

    def extractFeatures(self, item, doc_words, fields=[]):
        s_tokens = []
        for f in fields:
            tokens = None
            try:
                tokens = word_tokenize(item[f])
            except (KeyError, TypeError):
                pass

            if tokens:
                s_tokens.extend(tokens)

        s_features = []
        for token in doc_words:
            if token in s_tokens:
                s_features.append(1)
            else:
                s_features.append(0)

        if sum(s_features) <= self.COMPLETENESS_THRESHOLD:
            return None

        return s_features

    def defineFeatures(self, data, fields=[]):
        """
        define the global bag of words features
        """
        ngram_freq = {}

        for d in data:
            for r in d:
                for f in fields:
                    tokens = None
                    try:
                        tokens = word_tokenize(r[f])
                    except (KeyError, TypeError):
                        pass

                    if tokens:
                        for t in [t.lower() for t in tokens if t.lower() not in self.stopwords]:
                            t = t.strip('.')
                            ngram_freq[t] = ngram_freq.get(t, 0) + 1

        ngram_freq = dict([(w,c) for w,c in ngram_freq.items() if c > 1])
        if self.DEBUG:
            print "Global vocabulary: %s" % len(ngram_freq)
        return ngram_freq

    def get_stopwords(self):
        sw = stopwords.words('english')
        sw.extend([',', '\xe2', '.', ')', '(', ':', "'s", "'nt", '\x99', '\x86', '\xae', '\x92'])
        return sw

    def parseCSV(self, filename):
        """
        parseCSV: parses the CSV file to a dict
        """
        csv_reader = csv.DictReader(open(filename))
        return [r for r in csv_reader]

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-n","--number", action="store", type="int", dest="num_matches",default=10,help="number of results to return")
    parser.add_option("-s","--student", action="store", type="string", dest="student_file",default="unmatched_students.csv",help="csv of student data")
    parser.add_option("-t","--startup", action="store", type="string", dest="startup_file",default="unmatched_top_startups.csv",help="csv of startup data")
    (options, args) = parser.parse_args()

    h = HackMatch(num_matches=options.num_matches, student_file=options.student_file, startup_file=options.startup_file)

from nltk import download
download('stopwords')

#!/usr/bin/env python
# encoding: utf-8
# recognize.
"""
hackmatch.py

Created by Hilary Mason, Chris Wiggins, and Evan Korth.
Copyright (c) 2010 hackNY. All rights reserved.
"""
# pylint: disable=W0614
# pylint: disable=C0301

from collections import defaultdict
from optparse import OptionParser
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from hcluster import jaccard
from operator import itemgetter
from csv import DictReader

# startups: Name,E-mail,Company,In NYC,Funding,Site,Blog,Twitter,Num Employees,Environment,Project,Skills,Misc
# students: Student Name,e-mail,University,Major,Degree,Graduation Date,Site,Blog,Twitter,Facebook,Project,Skills,Misc

# Hack
# I'd like to write this:
#   return reduce(list.extend, list_of_lists)
# but it generates an error I don't get
def list_reducer(list_iter):
    result = []
    for l in list_iter:
        result.extend(l)
    return result

def get_stopwords():
    """
    get_stopwords: generate a list of stop words
    """
    return stopwords.words('english') + [',', '\xe2', '.', ')', '(', ':', "'s", "'nt", '\x99', '\x86', '\xae', '\x92']

def parse_csv(filename):
    """
    parse_csv: parses the CSV file to a dict
    """
    csv_reader = DictReader(open(filename))
    return [r for r in csv_reader]

def print_matches(matches, num_matches):
    """
    print_matches: print the top 'num_matches' matches
    """
    for key, value_dict in matches.items():
        print key
        all_matches = sorted(value_dict.items(), key=itemgetter(1))
        top_matches = all_matches[-num_matches:]
        for item, score in top_matches:
            print "\t%(item)s :: %(score)s" % locals()
            # print "'%s' '%s' %s" % (n.translate(string.maketrans("",""), string.punctuation), item.translate(string.maketrans("",""), string.punctuation), score)
        print '\n'

class HackMatch(object):
    """
    HackMatch: class to encapsulate matching companies versus startups on selected fields
    """
    DEBUG = False
    BOW_FIELDS = ['Environment', 'Project', 'Skills', 'Misc']
    COMPLETENESS_THRESHOLD = 4 # num of words necessary to match

    def __init__(self, student_file, startup_file, num_matches=3, distance=jaccard):
        self.stopwords = set(get_stopwords())
        self.distance = distance

        student_data = parse_csv(student_file)
        startup_data = parse_csv(startup_file)

        doc_words = self.define_features([student_data, startup_data], self.BOW_FIELDS)

        # matches = self.do_ranking(student_data, startup_data, doc_words, self.BOW_FIELDS, base_name_field='Student Name', match_name_field='Company')
        matches = self.do_ranking(startup_data, student_data, doc_words, self.BOW_FIELDS)

        print_matches(matches, num_matches)

    def do_ranking(self, base_data, match_data, doc_words, fields=None, base_name_field='Company', match_name_field='Student Name'):
        """
        do ranking
        """
        fields = fields or []
        base = dict((item[base_name_field], self.extract_features(item, doc_words, fields)) for item in base_data)

        matches = defaultdict(dict)
        for match_item in match_data:
            match_features = self.extract_features(match_item, doc_words, fields)
            temp_dict = matches[match_item[match_name_field]]
            for base_item, base_item_features in base.items(): # actually do the comparison
                if not base_item_features or not match_features:
                    temp_dict[base_item] = 0.0
                else:
                    temp_dict[base_item] = self.distance(base_item_features, match_features)
                if self.DEBUG:
                    print "%s :: %s = %s " % (match_item[match_name_field], base_item, self.distance(base_item_features, match_features))
        return matches

    def extract_features(self, item_dict, doc_words, fields=None):
        """
        extract_features: Determine whether features pass test
        """
        fields = fields or []
        tokeniter = (item_dict[f] for f in fields if f in item_dict)
        s_tokens = list_reducer(tokeniter)
        s_features = [token in s_tokens for token in doc_words]
        return s_features if sum(s_features) > self.COMPLETENESS_THRESHOLD else None

    def define_features(self, data, fields=None):
        """
        define the global bag of words features
        """
        fields = fields or []
        ngram_freq = defaultdict(int)

        featureiter = (
            r[f]
            for d in data
            for r in d
            for f in fields
            if f in r
        )
        for field in featureiter:
            tokeniter = (word.lower() for word in word_tokenize(field))
            legaliter = (word.strip('.') for word in tokeniter if word not in self.stopwords)
            for legal_word in legaliter:
                ngram_freq[legal_word] += 1
        ngram_freq = dict((word, word_count) for word, word_count in ngram_freq.items() if word_count > 1)
        if self.DEBUG:
            print "Global vocabulary: %s" % len(ngram_freq)
        return ngram_freq

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-n", "--number", action="store", type="int", dest="num_matches", default=10, help="number of results to return")
    parser.add_option("-s", "--student", action="store", type="string", dest="student_file", default="unmatched_students.csv", help="csv of student data")
    parser.add_option("-t", "--startup", action="store", type="string", dest="startup_file", default="unmatched_top_startups.csv", help="csv of startup data")
    (options, args) = parser.parse_args()

    hackmatch = HackMatch(num_matches=options.num_matches, student_file=options.student_file, startup_file=options.startup_file)


pep8 style

#!/usr/bin/python

from .pynma import PyNMA

#!/usr/bin/python

from xml.dom.minidom import parseString

try:
        from http.client import HTTPSConnection
except ImportError:
        from httplib import HTTPSConnection

try:
        from urllib.parse import urlencode
except ImportError:
        from urllib import urlencode

__version__ = "0.1"

API_SERVER = 'www.notifymyandroid.com'
ADD_PATH   = '/publicapi/notify'

USER_AGENT="PyNMA/v%s"%__version__

def uniq_preserve(seq): # Dave Kirby
    # Order preserving
    seen = set()
    return [x for x in seq if x not in seen and not seen.add(x)]

def uniq(seq):
    # Not order preserving
    return list({}.fromkeys(seq).keys())

class PyNMA(object):
    """PyNMA(apikey=[], developerkey=None)
takes 2 optional arguments:
 - (opt) apykey:      might me a string containing 1 key or an array of keys
 - (opt) developerkey: where you can store your developer key
"""

    def __init__(self, apikey=[], developerkey=None):
        self._developerkey = None
        self.developerkey(developerkey)
        if apikey:
            if type(apikey) == str:
                apikey = [apikey]
        self._apikey          = uniq(apikey)

    def addkey(self, key):
        "Add a key (register ?)"
        if type(key) == str:
            if not key in self._apikey:
                self._apikey.append(key)
        elif type(key) == list:
            for k in key:
                if not k in self._apikey:
                    self._apikey.append(k)

    def delkey(self, key):
        "Removes a key (unregister ?)"
        if type(key) == str:
            if key in self._apikey:
                self._apikey.remove(key)
        elif type(key) == list:
            for k in key:
                if key in self._apikey:
                    self._apikey.remove(k)

    def developerkey(self, developerkey):
        "Sets the developer key (and check it has the good length)"
        if type(developerkey) == str and len(developerkey) == 48:
            self._developerkey = developerkey

    def push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False):
        """Pushes a message on the registered API keys.
takes 5 arguments:
 - (req) application: application name [256]
 - (req) event:       event name       [1000]
 - (req) description: description      [10000]
 - (opt) url:         url              [512]
 - (opt) contenttype: Content Type (act: None (plain text) or text/html)
 - (opt) priority:    from -2 (lowest) to 2 (highest) (def:0)
 - (opt) batch_mode:  push to all keys at once (def:False)

 - (opt) html:        shortcut for contenttype=text/html

 - (opt) html:        shortcut for contenttype=text/html

Warning: using batch_mode will return error only if all API keys are bad
 cf: https://www.notifymyandroid.com/api.jsp
"""
        datas = {
            'application': application[:256].encode('utf8'),
            'event':       event[:1024].encode('utf8'),
            'description': description[:10000].encode('utf8'),
            'priority':    priority
        }

        if url:
            datas['url'] = url[:512]

        if contenttype == "text/html" or html == True: # Currently only accepted content type
            datas['content-type'] = "text/html"

        if self._developerkey:
            datas['developerkey'] = self._developerkey

        results = {}

        if not batch_mode:
            for key in self._apikey:
                datas['apikey'] = key
                res = self.callapi('POST', ADD_PATH, datas)
                results[key] = res
        else:
            datas['apikey'] = ",".join(self._apikey)
            res = self.callapi('POST', ADD_PATH, datas)
            results[datas['apikey']] = res
        return results

    def callapi(self, method, path, args):
        headers = { 'User-Agent': USER_AGENT }
        if method == "POST":
            headers['Content-type'] = "application/x-www-form-urlencoded"
        http_handler = HTTPSConnection(API_SERVER)
        http_handler.request(method, path, urlencode(args), headers)
        resp = http_handler.getresponse()

        try:
            res = self._parse_reponse(resp.read())
        except Exception as e:
            res = {'type':    "pynmaerror",
                   'code':    600,
                   'message': str(e)
                   }
            pass

        return res

    def _parse_reponse(self, response):
        root = parseString(response).firstChild
        for elem in root.childNodes:
            if elem.nodeType == elem.TEXT_NODE: continue
            if elem.tagName == 'success':
                res = dict(list(elem.attributes.items()))
                res['message'] = ""
                res['type']    = elem.tagName
                return res
            if elem.tagName == 'error':
                res = dict(list(elem.attributes.items()))
                res['message'] = elem.firstChild.nodeValue
                res['type']    = elem.tagName
                return res

#!/usr/bin/env python
from setuptools import setup, find_packages

setup(
    name='pyrowl',
    version='0.1',
    packages=find_packages()
)

#!/usr/bin/python

from pynma import PyNMA
from pprint import pprint
import os

p = None

def main(keys):
    global p
    pkey = None

    p = PyNMA()
    if os.path.isfile("mydeveloperkey"):
        dkey = open("mydeveloperkey",'r').readline().strip()
        p.developerkey(dkey)

    p.addkey(keys)
    res = p.push("test app", 'test event', 'test msg google', 'http://example.com', batch_mode=False, html=True)
    pprint(res)

if __name__ == "__main__":
    if os.path.isfile('myapikey'):
        keys = [_f for _f in open("myapikey",'r').read().split("\n") if _f]

        main(keys)
    else:
        print("need a file named myapikey containing one apikey per line")

#!/usr/bin/python

from xml.dom.minidom import parseString

try:
        from http.client import HTTPSConnection
except ImportError:
        from httplib import HTTPSConnection

try:
        from urllib.parse import urlencode
except ImportError:
        from urllib import urlencode

__version__ = "0.1"

API_SERVER = 'www.notifymyandroid.com'
ADD_PATH = '/publicapi/notify'

USER_AGENT = "PyNMA/v%s" % __version__

def uniq_preserve(seq):  # Dave Kirby
    # Order preserving
    seen = set()
    return [x for x in seq if x not in seen and not seen.add(x)]

def uniq(seq):
    # Not order preserving
    return list({}.fromkeys(seq).keys())

class PyNMA(object):
    """PyNMA(apikey=[], developerkey=None)
takes 2 optional arguments:
 - (opt) apykey:      might me a string containing 1 key or an array of keys
 - (opt) developerkey: where you can store your developer key
"""

    def __init__(self, apikey=[], developerkey=None):
        self._developerkey = None
        self.developerkey(developerkey)
        if apikey:
            if type(apikey) == str:
                apikey = [apikey]
        self._apikey = uniq(apikey)

    def addkey(self, key):
        "Add a key (register ?)"
        if type(key) == str:
            if key not in self._apikey:
                self._apikey.append(key)
        elif type(key) == list:
            for k in key:
                if k not in self._apikey:
                    self._apikey.append(k)

    def delkey(self, key):
        "Removes a key (unregister ?)"
        if type(key) == str:
            if key in self._apikey:
                self._apikey.remove(key)
        elif type(key) == list:
            for k in key:
                if key in self._apikey:
                    self._apikey.remove(k)

    def developerkey(self, developerkey):
        "Sets the developer key (and check it has the good length)"
        if type(developerkey) == str and len(developerkey) == 48:
            self._developerkey = developerkey

    def push(self, application="", event="", description="", url="",
             contenttype=None, priority=0, batch_mode=False, html=False):
        """Pushes a message on the registered API keys.
takes 5 arguments:
 - (req) application: application name [256]
 - (req) event:       event name       [1000]
 - (req) description: description      [10000]
 - (opt) url:         url              [512]
 - (opt) contenttype: Content Type (act: None (plain text) or text/html)
 - (opt) priority:    from -2 (lowest) to 2 (highest) (def:0)
 - (opt) batch_mode:  push to all keys at once (def:False)

 - (opt) html:        shortcut for contenttype=text/html

 - (opt) html:        shortcut for contenttype=text/html

Warning: using batch_mode will return error only if all API keys are bad
 cf: https://www.notifymyandroid.com/api.jsp
"""
        datas = {
            'application': application[:256].encode('utf8'),
            'event':       event[:1024].encode('utf8'),
            'description': description[:10000].encode('utf8'),
            'priority':    priority
        }

        if url:
            datas['url'] = url[:512]

        # Currently only accepted content type
        if contenttype == "text/html" or html is True:
            datas['content-type'] = "text/html"

        if self._developerkey:
            datas['developerkey'] = self._developerkey

        results = {}

        if not batch_mode:
            for key in self._apikey:
                datas['apikey'] = key
                res = self.callapi('POST', ADD_PATH, datas)
                results[key] = res
        else:
            datas['apikey'] = ",".join(self._apikey)
            res = self.callapi('POST', ADD_PATH, datas)
            results[datas['apikey']] = res
        return results

    def callapi(self, method, path, args):
        headers = {'User-Agent': USER_AGENT}
        if method == "POST":
            headers['Content-type'] = "application/x-www-form-urlencoded"
        http_handler = HTTPSConnection(API_SERVER)
        http_handler.request(method, path, urlencode(args), headers)
        resp = http_handler.getresponse()

        try:
            res = self._parse_reponse(resp.read())
        except Exception as e:
            res = {'type':    "pynmaerror",
                   'code':    600,
                   'message': str(e)
                   }
            pass

        return res

    def _parse_reponse(self, response):
        root = parseString(response).firstChild
        for elem in root.childNodes:
            if elem.nodeType == elem.TEXT_NODE:
                continue
            if elem.tagName == 'success':
                res = dict(list(elem.attributes.items()))
                res['message'] = ""
                res['type'] = elem.tagName
                return res
            if elem.tagName == 'error':
                res = dict(list(elem.attributes.items()))
                res['message'] = elem.firstChild.nodeValue
                res['type'] = elem.tagName
                return res

#!/usr/bin/python

from pynma import PyNMA
from pprint import pprint
import os

p = None

def main(keys):
    global p

    p = PyNMA()
    if os.path.isfile("mydeveloperkey"):
        dkey = open("mydeveloperkey", 'r').readline().strip()
        p.developerkey(dkey)

    p.addkey(keys)
    res = p.push("test app", 'test event',
                 'test msg google',
                 'http://example.com', batch_mode=False, html=True)
    pprint(res)

if __name__ == "__main__":
    if os.path.isfile('myapikey'):
        keys = [_f for _f in open("myapikey", 'r').read().split("\n") if _f]

        main(keys)
    else:
        print("need a file named myapikey containing one apikey per line")


Hi! I fixed some code for you!

var fs = require('fs'),
	path = require('path')

var data = {},
	dataDumpFile = './_development-engine-dump.json'

if (path.existsSync(dataDumpFile)) {
	console.log('node engine found ' + dataDumpFile + ' - loading data...')
	data = JSON.parse(fs.readFileSync(dataDumpFile))
	console.log('done loading data')
}

process.on('exit', function() {
	console.log('node storage engine detected shutdown')
	dumpData()
})

function dumpData() {
	console.log('dumping data...')
	fs.writeFileSync(dataDumpFile, JSON.stringify(data))
	console.log('done dumping data.')
}

setInterval(dumpData, 60000)

function typeError(operation, type, key) {
	return '"'+operation+'" expected a '+type+' at key "'+key+'" but found a '+typeof data[key]
}

var storeAPI = module.exports = {
	/* Getters
	 *********/
	getBytes: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, null)
		} else if (typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean') {
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('getBytes', 'string or number', key))
		}
	},

	getListItems: function(key, from, to, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, [])
		} else if (!(data[key] instanceof Array)) {
			callback && callback(typeError('getListItems', 'list', key))
		} else {
			if (to < 0) { to = data[key].length + to + 1 }
			from = Math.max(from, 0)
			to = Math.min(to, data[key].length)
			callback && callback(null, data[key].slice(from, to - from))
		}
	},

	getMembers: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, [])
		} else if (typeof data[key] != 'object') {
			callback && callback(typeError('getMembers', 'set', key))
		} else {
			var response = []
			for (var value in data[key]) { response.push(JSON.parse(value)) }
			callback && callback(null, response)
		}
	},

	/* Mutation handlers
	 *******************/
	handleMutation: function(operation, key, args, callback) {
		var operationArgs = [key].concat(args)
		if (callback) { operationArgs.push(callback) }
		storeAPI[operation].apply(this, operationArgs)
	},

	transact: function(transactionFn) {
		// the development engine acts atomically. We assume node won't halt during an operation
		transactionFn()
	},

	set: function(key, value, callback) {
		if (typeof data[key] == 'undefined' || typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean') {
			data[key] = value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('set', 'string or number', key), null)
		}
	},

	push: function(key, values, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = [].concat(values)
			callback && callback(null, null)
		} else if (data[key] instanceof Array) {
			data[key] = data[key].concat(values)
			callback && callback(null, null)
		} else {
			callback && callback(typeError('push', 'list', key), null)
		}
	},

	unshift: function(key, values, callback) {
		var values = Array.prototype.slice.call(arguments, 1)
		if (typeof data[key] == 'undefined') {
			data[key] = [].concat(values)
			callback && callback(null, null)
		} else if (data[key] instanceof Array) {
			data[key] = values.concat(data[key])
			callback && callback(null, null)
		} else {
			callback && callback(typeError('push', 'list', key), null)
		}
	},

	increment: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = 1
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] += 1
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('increment', 'number', key), null)
		}
	},

	decrement: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = -1
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] -= 1
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('decrement', 'number', key), null)
		}
	},

	add: function(key, value, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = value
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] += value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('add', 'number', key), null)
		}
	},

	subtract: function(key, value, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = -value
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] -= value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('subtract', 'number', key), null)
		}
	},

	sadd: function(key, value, callback) {
		value = JSON.stringify(value)
		if (typeof data[key] == 'undefined') {
			data[key] = {}
			data[key][value] = true
			callback && callback(null, 1)
		} else if (typeof data[key] == 'object') {
			var response = data[key][value] ? 0 : 1
			data[key][value] = true
			callback && callback(null, response)
		} else {
			callback && callback(typeError('sadd', 'set', key), null)
		}
	},

	srem: function(key, value, callback) {
		value = JSON.stringify(value)
		if (typeof data[key] == 'undefined') {
			callback && callback(null, 0)
		} else if (typeof data[key] == 'object') {
			var response = data[key][value] ? 1 : 0
			delete data[key][value]
			callback && callback(null, response)
		} else {
			callback && callback(typeError('srem', 'set', key), null)
		}
	}
}

var fs = require('fs'),
	path = require('path')

var data = {},
	dataDumpFile = './_development-engine-dump.json'

if (fs.existsSync(dataDumpFile)) {
	console.log('node engine found ' + dataDumpFile + ' - loading data...')
	data = JSON.parse(fs.readFileSync(dataDumpFile))
	console.log('done loading data')
}

process.on('exit', function() {
	console.log('node storage engine detected shutdown')
	dumpData()
})

function dumpData() {
	console.log('dumping data...')
	fs.writeFileSync(dataDumpFile, JSON.stringify(data))
	console.log('done dumping data.')
}

setInterval(dumpData, 60000)

function typeError(operation, type, key) {
	return '"'+operation+'" expected a '+type+' at key "'+key+'" but found a '+typeof data[key]
}

var storeAPI = module.exports = {
	/* Getters
	 *********/
	getBytes: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, null)
		} else if (typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean') {
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('getBytes', 'string or number', key))
		}
	},

	getListItems: function(key, from, to, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, [])
		} else if (!(data[key] instanceof Array)) {
			callback && callback(typeError('getListItems', 'list', key))
		} else {
			if (to < 0) { to = data[key].length + to + 1 }
			from = Math.max(from, 0)
			to = Math.min(to, data[key].length)
			callback && callback(null, data[key].slice(from, to - from))
		}
	},

	getMembers: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			callback && callback(null, [])
		} else if (typeof data[key] != 'object') {
			callback && callback(typeError('getMembers', 'set', key))
		} else {
			var response = []
			for (var value in data[key]) { response.push(JSON.parse(value)) }
			callback && callback(null, response)
		}
	},

	/* Mutation handlers
	 *******************/
	handleMutation: function(operation, key, args, callback) {
		var operationArgs = [key].concat(args)
		if (callback) { operationArgs.push(callback) }
		storeAPI[operation].apply(this, operationArgs)
	},

	transact: function(transactionFn) {
		// the development engine acts atomically. We assume node won't halt during an operation
		transactionFn()
	},

	set: function(key, value, callback) {
		if (typeof data[key] == 'undefined' || typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean') {
			data[key] = value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('set', 'string or number', key), null)
		}
	},

	push: function(key, values, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = [].concat(values)
			callback && callback(null, null)
		} else if (data[key] instanceof Array) {
			data[key] = data[key].concat(values)
			callback && callback(null, null)
		} else {
			callback && callback(typeError('push', 'list', key), null)
		}
	},

	unshift: function(key, values, callback) {
		var values = Array.prototype.slice.call(arguments, 1)
		if (typeof data[key] == 'undefined') {
			data[key] = [].concat(values)
			callback && callback(null, null)
		} else if (data[key] instanceof Array) {
			data[key] = values.concat(data[key])
			callback && callback(null, null)
		} else {
			callback && callback(typeError('push', 'list', key), null)
		}
	},

	increment: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = 1
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] += 1
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('increment', 'number', key), null)
		}
	},

	decrement: function(key, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = -1
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] -= 1
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('decrement', 'number', key), null)
		}
	},

	add: function(key, value, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = value
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] += value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('add', 'number', key), null)
		}
	},

	subtract: function(key, value, callback) {
		if (typeof data[key] == 'undefined') {
			data[key] = -value
			callback && callback(null, data[key])
		} else if (typeof data[key] == 'number') {
			data[key] -= value
			callback && callback(null, data[key])
		} else {
			callback && callback(typeError('subtract', 'number', key), null)
		}
	},

	sadd: function(key, value, callback) {
		value = JSON.stringify(value)
		if (typeof data[key] == 'undefined') {
			data[key] = {}
			data[key][value] = true
			callback && callback(null, 1)
		} else if (typeof data[key] == 'object') {
			var response = data[key][value] ? 0 : 1
			data[key][value] = true
			callback && callback(null, response)
		} else {
			callback && callback(typeError('sadd', 'set', key), null)
		}
	},

	srem: function(key, value, callback) {
		value = JSON.stringify(value)
		if (typeof data[key] == 'undefined') {
			callback && callback(null, 0)
		} else if (typeof data[key] == 'object') {
			var response = data[key][value] ? 1 : 0
			delete data[key][value]
			callback && callback(null, response)
		} else {
			callback && callback(typeError('srem', 'set', key), null)
		}
	}
}