4
0
Fork 0
forked from axeman/erpnext-nix

WIP: feat: apps initial commit #1

Draft
b12f wants to merge 1 commit from apps into main
15 changed files with 913 additions and 45 deletions

27
apps/eu_einvoice.nix Normal file
View file

@ -0,0 +1,27 @@
{ stdenv
, lib
, fetchFromGitHub
}:
stdenv.mkDerivation rec {
pname = "eu_einvoice";
version = "15.1.1";
src = fetchFromGitHub {
owner = "alyf-de";
repo = "eu_einvoice";
rev = "v${version}";
sha256 = "sha256-71XuVvmXr6T/OhAtSpV6YObx3PWW8rl4ZKQmujnlJXI=";
};
installPhase = ''
cp -r $src $out
'';
meta = with lib; {
homepage = "https://github.com/alyf-de/eu_einvoice";
description = "Create and import e-invoices with ERPNext. ";
license = licenses.gpl3;
maintainers = [ ];
};
}

3
apps/overlay.nix Normal file
View file

@ -0,0 +1,3 @@
final: prev: {
eu_einvoice = final.callPackage ./eu_einvoice.nix {};
}

8
flake.lock generated
View file

@ -91,16 +91,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1704295289,
"narHash": "sha256-9WZDRfpMqCYL6g/HNWVvXF0hxdaAgwgIGeLYiOhmes8=",
"lastModified": 1736061677,
"narHash": "sha256-DjkQPnkAfd7eB522PwnkGhOMuT9QVCZspDpJJYyOj60=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b0b2c5445c64191fd8d0b31f2b1a34e45a64547d",
"rev": "cbd8ec4de4469333c82ff40d057350c30e9f7d36",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}

View file

@ -2,7 +2,7 @@
description = "Dev Setup";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
systems.url = "github:nix-systems/default";
@ -24,6 +24,7 @@
overlays = [
self.overlays.default
self.overlays.pythonOverlay
self.overlays.apps
agenix.overlays.default
devshell.overlays.default
# https://github.com/NixOS/nixpkgs/issues/265675#issuecomment-1846591842
@ -38,6 +39,7 @@
overlays = {
default = (import ./overlay.nix);
pythonOverlay = (import ./python-overlay.nix);
apps = (import ./apps/overlay.nix);
};
devShells = eachSystem (system:
let

View file

@ -9,9 +9,9 @@ in
# interface
options.services.erpnext = {
enable = mkOption {
type = lib.types.bool;
type = types.bool;
default = false;
description = lib.mdDoc ''
description = mdDoc ''
Enable ERPNext.
When started, the ERPNext database is automatically created if it doesn't
@ -22,7 +22,7 @@ in
domain = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc ''
description = mdDoc ''
Domain name of your server.
'';
};
@ -30,50 +30,57 @@ in
workDir = mkOption {
type = types.str;
default = "/var/lib/erpnext";
description = lib.mdDoc "Working directory of ERPNext.";
description = mdDoc "Working directory of ERPNext.";
};
benchDir = mkOption {
type = types.str;
default = "${cfg.workDir}/bench";
description = lib.mdDoc "Bench directory for ERPNext.";
description = mdDoc "Bench directory for ERPNext.";
};
adminPasswordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/secrets/erpnext-admin-password";
description = lib.mdDoc ''
description = mdDoc ''
A file containing the Administrator user password.
'';
};
apps = mkOption {
description = ''
Apps to install into ERPNext.
'';
type = types.listOf types.package;
};
database = {
host = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc "Database host address.";
description = mdDoc "Database host address.";
};
port = mkOption {
type = types.port;
default = 3306;
description = lib.mdDoc "Database host port.";
description = mdDoc "Database host port.";
};
name = mkOption {
type = types.str;
default = "erpnext";
description = lib.mdDoc "Database name.";
description = mdDoc "Database name.";
};
user = mkOption {
type = types.str;
default = "erpnext";
description = lib.mdDoc "Database username.";
description = mdDoc "Database username.";
};
userPasswordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/secrets/erpnext-db-user-password";
description = lib.mdDoc ''
description = mdDoc ''
A file containing the MariaDB erpnext user password.
'';
};
@ -81,17 +88,17 @@ in
type = types.nullOr types.path;
default = null;
example = "/run/secrets/erpnext-db-root-password";
description = lib.mdDoc ''
description = mdDoc ''
A file containing the MariaDB root user password.
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
description = mdDoc "Create the database and database user locally.";
};
automaticMigrations = mkEnableOption
(lib.mdDoc "automatic migrations for database schema and data") // {
(mdDoc "automatic migrations for database schema and data") // {
default = true;
};
};
@ -100,17 +107,17 @@ in
host = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc "Redis host address.";
description = mdDoc "Redis host address.";
};
port = mkOption {
type = types.port;
default = 6379;
description = lib.mdDoc "Redis host port.";
description = mdDoc "Redis host port.";
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the redis server locally.";
description = mdDoc "Create the redis server locally.";
};
};
@ -118,19 +125,19 @@ in
bindAddress = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc "Web interface address.";
description = mdDoc "Web interface address.";
};
bindPort = mkOption {
type = types.port;
default = 9090;
description = lib.mdDoc "Web interface port.";
description = mdDoc "Web interface port.";
};
};
caddy = mkOption {
type = types.nullOr types.attrs;
default = null;
example = lib.literalExpression ''
example = literalExpression ''
{
serverAliases = [
"erpnext.your.domain"
@ -142,7 +149,7 @@ in
'';
}
'';
description = lib.mdDoc ''
description = mdDoc ''
With this option, you can customize a caddy virtual host.
Set to {} if you do not need any customization to the virtual host.
If enabled, then by default, the {option}`hostName` is
@ -160,14 +167,14 @@ in
user = mkOption {
type = types.str;
default = defaultUser;
description = lib.mdDoc "User under which ERPNext runs.";
description = mdDoc "User under which ERPNext runs.";
};
package = mkOption {
type = types.package;
default = pkgs.python3.pkgs.erpnext;
defaultText = literalExpression "pkgs.python3.pkgs.erpnext";
description = lib.mdDoc "The ERPNext package to use.";
description = mdDoc "The ERPNext package to use.";
};
};
@ -184,7 +191,7 @@ in
appsFile = pkgs.writeText "erpnext-apps.txt" ''
frappe
erpnext
'';
'' + lib.lists.fold (app: string: string + "\n${app.name}") "" cfg.apps;
# In a module, this could be provided by a use as a file as it could
# contain secrets and we don't want this in the nix-store. But here it
# is OK.
@ -215,7 +222,7 @@ in
"${pkgs.frappe-erpnext-assets}/share/sites/assets:${cfg.benchDir}/sites/assets"
"${appsFile}:${cfg.benchDir}/sites/apps.txt"
"${penv}:${cfg.benchDir}/env"
];
] ++ lib.lists.map (app: "${app}:${cfg.benchDir}/apps/${app.name}") cfg.apps;
WorkingDirectory = "${cfg.benchDir}";
# Expands to /var/lib/erpnext, see: 'man 5 systemd.exec'
StateDirectory = "erpnext";
@ -324,7 +331,7 @@ in
serviceConfig = defaultServiceConfig // {
TimeoutStartSec = "300s";
Restart = "on-failure";
ExecStartPre = assert cfg.adminPasswordFile != null && cfg.database.rootPasswordFile != null; pkgs.writeScript "erpnext-web-init" ''
ExecStartPre = assert cfg.adminPasswordFile != null && cfg.database.rootPasswordFile != null; pkgs.writeScript "erpnext-web-init" (''
#!/bin/sh
if ! test -e ${escapeShellArg "${cfg.workDir}/.db-created"}; then
# Fail on error
@ -352,7 +359,10 @@ in
# Migrate the database
${penv}/bin/bench --site ${cfg.domain} migrate
''}
'';
'' + lib.lists.fold (app: str: str + ''
${penv}/bin/bench --install-app ${app.name}
'') "" cfg.apps);
ExecStart = ''
${penv}/bin/gunicorn \
--chdir="${cfg.benchDir}/sites" \

View file

@ -10,8 +10,8 @@ let
mkdir -p $out/lib
mkdir -p $out/bin
ls -la ${path}/pkgs/development/tools/yarn2nix-moretea/**/*
cp ${path}/pkgs/development/tools/yarn2nix-moretea/yarn2nix/lib/urlToName.js $out/lib/urlToName.js
cp ${path}/pkgs/development/tools/yarn2nix-moretea/yarn2nix/internal/fixup_yarn_lock.js $out/bin/fixup_yarn_lock
patchShebangs $out
'';

View file

@ -18,8 +18,12 @@ final: prev: {
email-reply-parser = pyFinal.callPackage ./python/email-reply-parser.nix {};
maxminddb-geolite2 = pyFinal.callPackage ./python/maxminddb-geolite2.nix {};
psycopg2-binary = pyFinal.callPackage ./python/psycopg2-binary.nix {};
premailer = pyFinal.callPackage ./python/premailer.nix {};
rauth = pyFinal.callPackage ./python/rauth.nix {};
traceback-with-variables = pyFinal.callPackage ./python/traceback-with-variables.nix {};
pydantic = pyFinal.callPackage ./python/pydantic.nix {};
# pydantic = pyFinal.callPackage ./python/pydantic.nix {};
hatchling = pyFinal.callPackage ./python/hatchling.nix {};
nose = pyFinal.callPackage ./python/nose.nix {};
versioningit = pyPrev.versioningit.overridePythonAttrs (oldAttrs: (rec {
version = "2.2.1";
@ -31,7 +35,7 @@ final: prev: {
}));
fastapi = pyPrev.fastapi.overridePythonAttrs (oldAttrs: (rec {
propagatedBuildInputs = oldAttrs.propagatedBuildInputs ++ [
propagatedBuildInputs = [
pyPrev.pydantic-settings
pyPrev.pydantic-extra-types
];

View file

@ -0,0 +1,576 @@
diff --git a/LICENSE.cpython b/LICENSE.cpython
new file mode 100644
index 0000000..14603b9
--- /dev/null
+++ b/LICENSE.cpython
@@ -0,0 +1,277 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC. Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team. In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation. In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see https://opensource.org for
+the Open Source Definition). Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+ Release Derived Year Owner GPL-
+ from compatible? (1)
+
+ 0.9.0 thru 1.2 1991-1995 CWI yes
+ 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
+ 1.6 1.5.2 2000 CNRI no
+ 2.0 1.6 2000 BeOpen.com no
+ 1.6.1 1.6 2001 CNRI yes (2)
+ 2.1 2.0+1.6.1 2001 PSF no
+ 2.0.1 2.0+1.6.1 2001 PSF yes
+ 2.1.1 2.1+2.0.1 2001 PSF yes
+ 2.1.2 2.1.1 2002 PSF yes
+ 2.1.3 2.1.2 2002 PSF yes
+ 2.2 and above 2.1.1 2001-now PSF yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+ the GPL. All Python licenses, unlike the GPL, let you distribute
+ a modified version without making your changes open source. The
+ GPL-compatible licenses make it possible to combine Python with
+ other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+ because its license has a choice of law clause. According to
+ CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+ is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+Python software and documentation are licensed under the
+Python Software Foundation License Version 2.
+
+Starting with Python 3.8.6, examples, recipes, and other code in
+the documentation are dual licensed under the PSF License Version 2
+and the Zero-Clause BSD license.
+
+Some software incorporated into Python is under different licenses.
+The licenses are listed with code falling under that license.
+
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001-2024 Python Software Foundation; All Rights Reserved"
+are retained in Python alone or in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions. Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee. This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party. As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee. Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement. This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013. This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee. This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+ ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands. All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
+----------------------------------------------------------------------
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/nose/importer.py b/nose/importer.py
index e677658..77099eb 100644
--- a/nose/importer.py
+++ b/nose/importer.py
@@ -7,9 +7,10 @@ the builtin importer.
import logging
import os
import sys
+import tokenize
from nose.config import Config
-
-from imp import find_module, load_module, acquire_lock, release_lock
+from importlib import _imp
+from importlib import machinery
log = logging.getLogger(__name__)
@@ -20,6 +21,244 @@ except AttributeError:
return (os.path.normcase(os.path.realpath(src)) ==
os.path.normcase(os.path.realpath(dst)))
+################################################################################
+# BEGIN IMPORTLIB SHIMS
+################################################################################
+
+# Adapted from the CPython 3.11 imp.py code.
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved
+# Originally licensed under the PSLv2 (see LICENSE.cpython) and incorporated under the LGPL 2.1 (see lgpl.txt).
+
+try:
+ from _imp import create_dynamic
+except ImportError:
+ # Platform doesn't support dynamic loading.
+ create_dynamic = None
+
+from importlib._bootstrap import _ERR_MSG, _exec, _load, _builtin_from_name
+from importlib._bootstrap_external import SourcelessFileLoader
+
+from importlib import machinery
+from importlib import util
+import importlib
+import os
+import sys
+import tokenize
+import types
+
+
+SEARCH_ERROR = 0
+PY_SOURCE = 1
+PY_COMPILED = 2
+C_EXTENSION = 3
+PY_RESOURCE = 4
+PKG_DIRECTORY = 5
+C_BUILTIN = 6
+PY_FROZEN = 7
+PY_CODERESOURCE = 8
+IMP_HOOK = 9
+
+
+def get_suffixes():
+ extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES]
+ source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
+ bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES]
+
+ return extensions + source + bytecode
+
+
+class _HackedGetData:
+
+ """Compatibility support for 'file' arguments of various load_*()
+ functions."""
+
+ def __init__(self, fullname, path, file=None):
+ super().__init__(fullname, path)
+ self.file = file
+
+ def get_data(self, path):
+ """Gross hack to contort loader to deal w/ load_*()'s bad API."""
+ if self.file and path == self.path:
+ # The contract of get_data() requires us to return bytes. Reopen the
+ # file in binary mode if needed.
+ if not self.file.closed:
+ file = self.file
+ if 'b' not in file.mode:
+ file.close()
+ if self.file.closed:
+ self.file = file = open(self.path, 'rb')
+
+ with file:
+ return file.read()
+ else:
+ return super().get_data(path)
+
+
+class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader):
+
+ """Compatibility support for implementing load_source()."""
+
+
+def load_source(name, pathname, file=None):
+ loader = _LoadSourceCompatibility(name, pathname, file)
+ spec = util.spec_from_file_location(name, pathname, loader=loader)
+ if name in sys.modules:
+ module = _exec(spec, sys.modules[name])
+ else:
+ module = _load(spec)
+ # To allow reloading to potentially work, use a non-hacked loader which
+ # won't rely on a now-closed file object.
+ module.__loader__ = machinery.SourceFileLoader(name, pathname)
+ module.__spec__.loader = module.__loader__
+ return module
+
+
+class _LoadCompiledCompatibility(_HackedGetData, SourcelessFileLoader):
+
+ """Compatibility support for implementing load_compiled()."""
+
+
+def load_compiled(name, pathname, file=None):
+ loader = _LoadCompiledCompatibility(name, pathname, file)
+ spec = util.spec_from_file_location(name, pathname, loader=loader)
+ if name in sys.modules:
+ module = _exec(spec, sys.modules[name])
+ else:
+ module = _load(spec)
+ # To allow reloading to potentially work, use a non-hacked loader which
+ # won't rely on a now-closed file object.
+ module.__loader__ = SourcelessFileLoader(name, pathname)
+ module.__spec__.loader = module.__loader__
+ return module
+
+
+def load_package(name, path):
+ if os.path.isdir(path):
+ extensions = (machinery.SOURCE_SUFFIXES[:] +
+ machinery.BYTECODE_SUFFIXES[:])
+ for extension in extensions:
+ init_path = os.path.join(path, '__init__' + extension)
+ if os.path.exists(init_path):
+ path = init_path
+ break
+ else:
+ raise ValueError('{!r} is not a package'.format(path))
+ spec = util.spec_from_file_location(name, path,
+ submodule_search_locations=[])
+ if name in sys.modules:
+ return _exec(spec, sys.modules[name])
+ else:
+ return _load(spec)
+
+
+def load_module(name, file, filename, details):
+ """
+
+ Load a module, given information returned by find_module().
+
+ The module name must include the full package name, if any.
+
+ """
+ suffix, mode, type_ = details
+ if mode and (not mode.startswith('r') or '+' in mode):
+ raise ValueError('invalid file open mode {!r}'.format(mode))
+ elif file is None and type_ in {PY_SOURCE, PY_COMPILED}:
+ msg = 'file object required for import (type code {})'.format(type_)
+ raise ValueError(msg)
+ elif type_ == PY_SOURCE:
+ return load_source(name, filename, file)
+ elif type_ == PY_COMPILED:
+ return load_compiled(name, filename, file)
+ elif type_ == PKG_DIRECTORY:
+ return load_package(name, filename)
+ elif type_ == C_BUILTIN:
+ return init_builtin(name)
+ elif type_ == PY_FROZEN:
+ return _imp.init_frozen(name)
+ else:
+ msg = "Don't know how to import {} (type code {})".format(name, type_)
+ raise ImportError(msg, name=name)
+
+
+def find_module(name, path=None):
+ """
+
+ Search for a module.
+
+ If path is omitted or None, search for a built-in, frozen or special
+ module and continue search in sys.path. The module name cannot
+ contain '.'; to search for a submodule of a package, pass the
+ submodule name and the package's __path__.
+
+ """
+ if not isinstance(name, str):
+ raise TypeError("'name' must be a str, not {}".format(type(name)))
+ elif not isinstance(path, (type(None), list)):
+ # Backwards-compatibility
+ raise RuntimeError("'path' must be None or a list, "
+ "not {}".format(type(path)))
+
+ if path is None:
+ if _imp.is_builtin(name):
+ return None, None, ('', '', C_BUILTIN)
+ elif _imp.is_frozen(name):
+ return None, None, ('', '', PY_FROZEN)
+ else:
+ path = sys.path
+
+ for entry in path:
+ package_directory = os.path.join(entry, name)
+ for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]:
+ package_file_name = '__init__' + suffix
+ file_path = os.path.join(package_directory, package_file_name)
+ if os.path.isfile(file_path):
+ return None, package_directory, ('', '', PKG_DIRECTORY)
+ for suffix, mode, type_ in get_suffixes():
+ file_name = name + suffix
+ file_path = os.path.join(entry, file_name)
+ if os.path.isfile(file_path):
+ break
+ else:
+ continue
+ break # Break out of outer loop when breaking out of inner loop.
+ else:
+ raise ImportError(_ERR_MSG.format(name), name=name)
+
+ encoding = None
+ if 'b' not in mode:
+ with open(file_path, 'rb') as file:
+ encoding = tokenize.detect_encoding(file.readline)[0]
+ file = open(file_path, mode, encoding=encoding)
+ return file, file_path, (suffix, mode, type_)
+
+
+def reload(module):
+ """
+
+ Reload the module and return it.
+
+ The module must have been successfully imported before.
+
+ """
+ return importlib.reload(module)
+
+
+def init_builtin(name):
+ """
+
+ Load and return a built-in module by name, or None is such module doesn't
+ exist
+ """
+ try:
+ return _builtin_from_name(name)
+ except ImportError:
+ return None
+
+
+################################################################################
+# END IMPORTLIB SHIMS
+################################################################################
+
class Importer(object):
"""An importer class that does only path-specific imports. That
@@ -73,7 +312,7 @@ class Importer(object):
else:
part_fqname = "%s.%s" % (part_fqname, part)
try:
- acquire_lock()
+ _imp.acquire_lock()
log.debug("find module part %s (%s) in %s",
part, part_fqname, path)
fh, filename, desc = find_module(part, path)
@@ -95,7 +334,7 @@ class Importer(object):
finally:
if fh:
fh.close()
- release_lock()
+ _imp.release_lock()
if parent:
setattr(parent, part, mod)
if hasattr(mod, '__path__'):
diff --git a/nose/result.py b/nose/result.py
index f974a14..228a42c 100644
--- a/nose/result.py
+++ b/nose/result.py
@@ -13,7 +13,7 @@ try:
# 2.7+
from unittest.runner import _TextTestResult
except ImportError:
- from unittest import _TextTestResult
+ from unittest import TextTestResult as _TextTestResult
from nose.config import Config
from nose.util import isclass, ln as _ln # backwards compat

View file

@ -44,7 +44,6 @@
, passlib
, pdfkit
, phonenumbers
, premailer
, psutil
, psycopg2-binary
, pydantic
@ -137,7 +136,6 @@ buildPythonPackage rec {
passlib
pdfkit
phonenumbers
premailer
psutil
psycopg2-binary
pydantic

75
python/hatchling.nix Normal file
View file

@ -0,0 +1,75 @@
{ lib
, buildPythonPackage
, fetchPypi
, pythonOlder
# runtime
, editables
, packaging
, pathspec
, pluggy
, tomli
, trove-classifiers
# tests
, build
, python
, requests
, virtualenv
}:
buildPythonPackage rec {
pname = "hatchling";
version = "1.21.0";
format = "pyproject";
disabled = pythonOlder "3.8";
src = fetchPypi {
inherit pname version;
hash = "sha256-XAhncjV6UHI7gl/V2lJ4rH42l833eX0HVBpskLb/dUw=";
};
# listed in backend/pyproject.toml
propagatedBuildInputs = [
editables
packaging
pathspec
pluggy
trove-classifiers
] ++ lib.optionals (pythonOlder "3.11") [
tomli
];
pythonImportsCheck = [
"hatchling"
"hatchling.build"
];
# tries to fetch packages from the internet
doCheck = false;
# listed in /backend/tests/downstream/requirements.txt
nativeCheckInputs = [
build
requests
virtualenv
];
preCheck = ''
export HOME=$TMPDIR
'';
checkPhase = ''
runHook preCheck
${python.interpreter} tests/downstream/integrate.py
runHook postCheck
'';
meta = with lib; {
description = "Modern, extensible Python build backend";
homepage = "https://hatch.pypa.io/latest/";
changelog = "https://github.com/pypa/hatch/releases/tag/hatchling-v${version}";
license = licenses.mit;
maintainers = with maintainers; [ hexa ofek ];
};
}

67
python/nose.nix Normal file
View file

@ -0,0 +1,67 @@
# Removed in nixpkgs, copied from there
#
# Ref: https://github.com/NixOS/nixpkgs/issues/326513
# drop PR: https://github.com/NixOS/nixpkgs/pull/348699
{
lib,
buildPythonPackage,
fetchPypi,
isPy3k,
isPyPy,
python,
python312,
coverage,
setuptools,
}:
buildPythonPackage rec {
version = "1.3.7";
pname = "nose";
pyproject = true;
src = fetchPypi {
inherit pname version;
sha256 = "f1bffef9cbc82628f6e7d7b40d7e255aefaa1adb6a1b1d26c69a8b79e6208a98";
};
build-system = [ setuptools ];
patches = lib.optional isPy3k [ ./0001-nose-python-3.12-fixes.patch ];
postPatch = ''
substituteInPlace setup.py \
--replace "'use_2to3': True," ""
substituteInPlace setup3lib.py \
--replace "from setuptools.command.build_py import Mixin2to3" "from distutils.util import Mixin2to3"
'';
# 2to3 is removed from Python 3.13, so always use Python 3.12 2to3 for now.
preBuild = lib.optionalString isPy3k ''
${python312.pythonOnBuildForHost}/bin/2to3 -wn nose functional_tests unit_tests
'';
propagatedBuildInputs = [ coverage ];
doCheck = false; # lot's of transient errors, too much hassle
checkPhase =
if isPy3k then
''
${python.pythonOnBuildForHost.interpreter} setup.py build_tests
''
else
""
+ ''
rm functional_tests/test_multiprocessing/test_concurrent_shared.py* # see https://github.com/nose-devs/nose/commit/226bc671c73643887b36b8467b34ad485c2df062
${python.pythonOnBuildForHost.interpreter} selftest.py
'';
meta = with lib; {
broken = isPyPy; # missing 2to3 conversion utility
description = "Unittest-based testing framework for python that makes writing and running tests easier";
mainProgram = "nosetests";
homepage = "https://nose.readthedocs.io/";
license = licenses.lgpl3;
maintainers = [ ];
};
}

47
python/premailer.nix Normal file
View file

@ -0,0 +1,47 @@
# Removed in nixpkgs, copied from there
#
# Ref: https://github.com/NixOS/nixpkgs/issues/326513
# drop PR: https://github.com/NixOS/nixpkgs/pull/348580
{
lib,
buildPythonPackage,
fetchPypi,
isPy27,
cssselect,
cssutils,
lxml,
mock,
nose,
requests,
cachetools,
}:
buildPythonPackage rec {
pname = "premailer";
version = "3.10.0";
format = "setuptools";
disabled = isPy27; # no longer compatible with urllib
src = fetchPypi {
inherit pname version;
sha256 = "d1875a8411f5dc92b53ef9f193db6c0f879dc378d618e0ad292723e388bfe4c2";
};
buildInputs = [
mock
nose
];
propagatedBuildInputs = [
cachetools
cssselect
cssutils
lxml
requests
];
meta = {
description = "Turns CSS blocks into style attributes";
homepage = "https://github.com/peterbe/premailer";
license = lib.licenses.bsd3;
};
}

56
python/rauth.nix Normal file
View file

@ -0,0 +1,56 @@
# Removed in nixpkgs, copied from there
#
# Ref: https://github.com/NixOS/nixpkgs/issues/326513
# drop PR: https://github.com/NixOS/nixpkgs/pull/330417
{
lib,
buildPythonPackage,
fetchFromGitHub,
fetchpatch,
requests,
pytestCheckHook,
mock,
nose,
pycrypto,
}:
buildPythonPackage rec {
pname = "rauth";
version = "0.7.2";
format = "setuptools";
src = fetchFromGitHub {
owner = "litl";
repo = "rauth";
rev = version;
hash = "sha256-wRKZbxZCEfihOaJM8sk8438LE++KJWxdOGImpL1gHa4=";
};
patches = [
(fetchpatch {
# https://github.com/litl/rauth/pull/211
name = "fix-pycrypdodome-replacement-for-pycrypto.patch";
url = "https://github.com/litl/rauth/commit/7fb3b7bf1a1869a52cf59ee3eb607d318e97265c.patch";
hash = "sha256-jiAIw+VQ2d/bkm2brqfY1RUrNGf+lsMPnoI91gGUS6o=";
})
];
propagatedBuildInputs = [ requests ];
pythonImportsCheck = [ "rauth" ];
nativeCheckInputs = [
pytestCheckHook
mock
nose
pycrypto
];
meta = with lib; {
description = "Python library for OAuth 1.0/a, 2.0, and Ofly";
homepage = "https://github.com/litl/rauth";
changelog = "https://github.com/litl/rauth/blob/${src.rev}/CHANGELOG";
license = licenses.mit;
maintainers = with maintainers; [ blaggacao ];
};
}

View file

@ -1,12 +1,12 @@
{
benchVersion = "5.19.0";
erpnextVersion = "15.9.1";
frappeVersion = "15.8.1";
benchVersion = "5.23.0";
erpnextVersion = "15.47.5";
frappeVersion = "15.40.4";
hashes = {
"benchSrcHash" = "sha256-y8nx4vFVQggwGv2MWQ88WczgVbPxPybZV38FF5u5aWI=";
"erpnextSrcHash" = "sha256-nkXN0PTcWt1nSy3eRdBF2h0WMdAC79qWzaj9kXRsG2I=";
"erpnextYarnHash" = "1farnqrfnzshpbpx4nyarw13g8m3389ix3hrc4661xxm887lz5fv";
"frappeSrcHash" = "sha256-FDUUNbULPmMY6dDgbMHrxXD8pK1AP+T7kG7mY9MmMDg=";
"frappeYarnHash" = "0rj2v69siagwjz632hyaii5ni24fp434cznaxpi8978fq07qx6l9";
"benchSrcHash" = "sha256-HGlYLo62EZERM0I2usKpw3ACkPpLxxwwmNQ+Rd9dsN4=";
"erpnextSrcHash" = "sha256-1DJIXldd/2gqxtYRTc1qFVdnNVmIy3Z+TAj+LC5DSYk=";
"erpnextYarnHash" = "";
"frappeSrcHash" = "sha256-QbMX1hyHkzxcEdcIMKPbU9qYNbdtpwH5wjXtcrA9q1Y=";
"frappeYarnHash" = "";
};
}

View file

@ -87,6 +87,9 @@
database.rootPasswordFile = config.age.secrets.erpnext-db-root-password.path;
database.userPasswordFile = config.age.secrets.erpnext-db-user-password.path;
caddy = {};
apps = [
pkgs.eu_einvoice
];
};
services.caddy = {
email = "admins@pub.solar";