diff --git a/flake.lock b/flake.lock index ad1352c4..589c2841 100644 --- a/flake.lock +++ b/flake.lock @@ -49,11 +49,11 @@ ] }, "locked": { - "lastModified": 1716202913, - "narHash": "sha256-zjPNXI4DWBOrPsrK8u/XTsm5Q36quONQvz0jhAKHEeg=", + "lastModified": 1694616124, + "narHash": "sha256-c49BVhQKw3XDRgt+y+uPAbArtgUlMXCET6VxEBmzHXE=", "owner": "nix-community", "repo": "gomod2nix", - "rev": "4702caff8e201f4c98fe3583637a930d253447c8", + "rev": "f95720e89af6165c8c0aa77f180461fe786f3c21", "type": "github" }, "original": { @@ -69,11 +69,11 @@ ] }, "locked": { - "lastModified": 1697051330, - "narHash": "sha256-WUdCSm7+ckKv3IvTOmP8TOyqIXKXCRNE2RCrGl5J1hU=", + "lastModified": 1724086528, + "narHash": "sha256-4AS91qKPtym6Z+IEAblk7CawrvRMI6QTW9Q2SZrEDAQ=", "owner": "replit", "repo": "nix-editor", - "rev": "2ecfe2bc906c07fba727ca00a784b88248eefef7", + "rev": "9472fbde811cbfedc8b2b649cb59a777d974da8d", "type": "github" }, "original": { @@ -84,11 +84,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724300212, - "narHash": "sha256-x3jl6OWTs+L9C7EtscuWZmGZWI0iSBDafvg3X7JMa1A=", + "lastModified": 1724999960, + "narHash": "sha256-LB3jqSGW5u1ZcUcX6vO/qBOq5oXHlmOCxsTXGMEitp4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4de4818c1ffa76d57787af936e8a23648bda6be4", + "rev": "b96f849e725333eb2b1c7f1cb84ff102062468ba", "type": "github" }, "original": { diff --git a/internal/backends/backends.go b/internal/backends/backends.go index b1edcb9f..5267bda6 100644 --- a/internal/backends/backends.go +++ b/internal/backends/backends.go @@ -29,6 +29,7 @@ import ( var languageBackends = []api.LanguageBackend{ python.PythonPoetryBackend, python.PythonPipBackend, + python.PythonUvBackend, nodejs.BunBackend, nodejs.NodejsNPMBackend, nodejs.NodejsPNPMBackend, diff --git a/internal/backends/python/python.go b/internal/backends/python/python.go index c3363bf6..209b01e0 100644 --- a/internal/backends/python/python.go +++ b/internal/backends/python/python.go @@ -58,17 +58,27 @@ type pyprojectTOMLGroup struct { // pyprojectTOML represents the relevant parts of a pyproject.toml // file. type pyprojectTOML struct { + BuildSystem *struct { + Requires []string `toml:"requires"` + BuildBackend string `toml:"build-backend"` + } `toml:"build-system"` + Project *struct { + Dependencies []string `toml:"dependencies"` + } `toml:"project"` Tool struct { Poetry *struct { - Name string `json:"name"` + Name string `toml:"name"` // interface{} because they can be either // strings or maps (why?? good lord). - Dependencies map[string]interface{} `json:"dependencies"` - DevDependencies map[string]interface{} `json:"dev-dependencies"` - Packages []pyprojectPackageCfg `json:"packages"` - Group map[string]pyprojectTOMLGroup `json:"group"` - } `json:"poetry"` - } `json:"tool"` + Dependencies map[string]interface{} `toml:"dependencies"` + DevDependencies map[string]interface{} `toml:"dev-dependencies"` + Packages []pyprojectPackageCfg `toml:"packages"` + Group map[string]pyprojectTOMLGroup `toml:"group"` + } `toml:"poetry"` + Uv *struct { + Sources map[string]interface{} `toml:"sources"` + } `toml:"uv"` + } `toml:"tool"` } // poetryLock represents the relevant parts of a poetry.lock file, in @@ -80,6 +90,28 @@ type poetryLock struct { } `json:"package"` } +type uvLock struct { + Version int `toml:"version"` + RequiresPython string `toml:"requires-python"` + Packages []struct { + Name string `toml:"name"` + Version string `toml:"version"` + Source struct { + Registry string `toml:"registry"` + } `toml:"source"` + Sdist struct { + URL string `toml:"url"` + Hash string `toml:"hash"` + Size int `toml:"size"` + } `toml:"sdist"` + Wheels []struct { + URL string `toml:"url"` + Hash string `toml:"hash"` + Size int `toml:"size"` + } `toml:"wheels"` + } `toml:"package"` +} + func pep440Join(name api.PkgName, spec api.PkgSpec) string { if spec == "" { return string(name) @@ -490,6 +522,14 @@ func makePythonPipBackend() api.LanguageBackend { b := api.LanguageBackend{ Name: "python3-pip", Specfile: "requirements.txt", + IsSpecfileCompatible: func(path string) (bool, error) { + cfg, err := readPyproject() + if err != nil { + return true, nil + } + + return cfg.Tool.Poetry == nil, nil + }, IsAvailable: func() bool { _, err := exec.LookPath("pip") return err == nil @@ -657,6 +697,151 @@ func makePythonPipBackend() api.LanguageBackend { return b } +// makePythonUvBackend returns a backend for invoking uv. +func makePythonUvBackend() api.LanguageBackend { + listUvSpecfile := func() map[api.PkgName]api.PkgSpec { + cfg, err := readPyproject() + if err != nil { + return nil + } + + pkgs := map[api.PkgName]api.PkgSpec{} + + for _, dep := range cfg.Project.Dependencies { + var name *api.PkgName + var spec *api.PkgSpec + + matches := matchPackageAndSpec.FindSubmatch([]byte(dep)) + if len(matches) > 1 { + _name := api.PkgName(string(matches[1])) + name = &_name + } + if len(matches) > 2 { + _spec := api.PkgSpec(string(matches[2])) + spec = &_spec + } else { + _spec := api.PkgSpec("") + spec = &_spec + } + pkgs[*name] = *spec + } + + return pkgs + } + b := api.LanguageBackend{ + Name: "python3-uv", + Specfile: "pyproject.toml", + IsSpecfileCompatible: func(path string) (bool, error) { + cfg, err := readPyproject() + if err != nil { + return false, err + } + + return cfg.Tool.Poetry == nil, nil + }, + Lockfile: "uv.lock", + IsAvailable: func() bool { + _, err := exec.LookPath("uv") + return err == nil + }, + Alias: "python-python3-uv", + FilenamePatterns: []string{"*.py"}, + Quirks: api.QuirksAddRemoveAlsoInstalls | api.QuirksAddRemoveAlsoLocks, + NormalizePackageArgs: normalizePackageArgs, + NormalizePackageName: normalizePackageName, + GetPackageDir: func() string { + pkgdir := commonGuessPackageDir() + if pkgdir != "" { + return pkgdir + } + + return "" + }, + SortPackages: pkg.SortPrefixSuffix(normalizePackageName), + + Search: searchPypi, + Info: info, + Add: func(ctx context.Context, pkgs map[api.PkgName]api.PkgSpec, projectName string) { + //nolint:ineffassign,wastedassign,staticcheck + span, ctx := tracer.StartSpanFromContext(ctx, "uv (init) add") + defer span.Finish() + // Initalize the specfile if it doesnt exist + if !util.Exists("pyproject.toml") { + cmd := []string{"uv", "init", "--no-progress"} + + if projectName != "" { + cmd = append(cmd, "--name", projectName) + } + + util.RunCmd(cmd) + } + + cmd := []string{"uv", "add"} + for name, spec := range pkgs { + if found, ok := moduleToPypiPackageAliases[string(name)]; ok { + delete(pkgs, name) + name = api.PkgName(found) + pkgs[api.PkgName(name)] = api.PkgSpec(spec) + } + + cmd = append(cmd, pep440Join(name, spec)) + } + util.RunCmd(cmd) + }, + Lock: func(ctx context.Context) { + //nolint:ineffassign,wastedassign,staticcheck + span, ctx := tracer.StartSpanFromContext(ctx, "poetry lock") + defer span.Finish() + util.RunCmd([]string{"uv", "lock"}) + }, + Remove: func(ctx context.Context, pkgs map[api.PkgName]bool) { + //nolint:ineffassign,wastedassign,staticcheck + span, ctx := tracer.StartSpanFromContext(ctx, "uv uninstall") + defer span.Finish() + + cmd := []string{"uv", "remove"} + for name := range pkgs { + cmd = append(cmd, string(name)) + } + util.RunCmd(cmd) + }, + Install: func(ctx context.Context) { + //nolint:ineffassign,wastedassign,staticcheck + span, ctx := tracer.StartSpanFromContext(ctx, "uv install") + defer span.Finish() + + util.RunCmd([]string{"uv", "sync"}) + }, + ListSpecfile: func(mergeAllGroups bool) map[api.PkgName]api.PkgSpec { + pkgs := listUvSpecfile() + return pkgs + }, + ListLockfile: func() map[api.PkgName]api.PkgVersion { + var cfg uvLock + if _, err := toml.DecodeFile("uv.lock", &cfg); err != nil { + util.DieProtocol("%s", err.Error()) + + } + pkgs := map[api.PkgName]api.PkgVersion{} + for _, pkgObj := range cfg.Packages { + pkgs[api.PkgName(pkgObj.Name)] = api.PkgVersion(pkgObj.Version) + } + return pkgs + }, + GuessRegexps: pythonGuessRegexps, + Guess: guess, + InstallReplitNixSystemDependencies: func(ctx context.Context, pkgs []api.PkgName) { + // Ignore the error here, because if we can't read the specfile, + // we still want to add the deps from above at least. + specfilePkgs := listUvSpecfile() + commonInstallNixDeps(ctx, pkgs, specfilePkgs) + }, + } + + return b +} + // PythonPoetryBackend is a UPM backend for Python 3 that uses Poetry. var PythonPoetryBackend = makePythonPoetryBackend() var PythonPipBackend = makePythonPipBackend() +var PythonUvBackend = makePythonUvBackend() diff --git a/nix/devshell/default.nix b/nix/devshell/default.nix index 88ec525c..d73e1997 100644 --- a/nix/devshell/default.nix +++ b/nix/devshell/default.nix @@ -10,6 +10,7 @@ python310Full, python310Packages, golangci-lint, + uv, }: mkShell { name = "upm"; @@ -24,5 +25,6 @@ mkShell { python310Packages.pip poetry python310Full + uv ]; } diff --git a/test-suite/Add_test.go b/test-suite/Add_test.go index 7ee72864..36ad345e 100644 --- a/test-suite/Add_test.go +++ b/test-suite/Add_test.go @@ -21,7 +21,7 @@ func TestAdd(t *testing.T) { case "bun": pkgs = []string{"lodash", "react", "@replit/protocol"} - case "python3-poetry", "python3-pip": + case "python3-poetry", "python3-pip", "python3-uv": pkgs = []string{"replit-ai", "flask >=2", "pyyaml", "discord-py 2.3.2"} default: diff --git a/test-suite/Guess_test.go b/test-suite/Guess_test.go index bb653aaa..018eb789 100644 --- a/test-suite/Guess_test.go +++ b/test-suite/Guess_test.go @@ -43,7 +43,7 @@ func TestGuess(t *testing.T) { } } - case "python3-poetry", "python3-pip": + case "python3-poetry", "python3-pip", "python3-uv": for _, ext := range []string{"py"} { _, ok := tests[ext] if !ok { diff --git a/test-suite/Info_test.go b/test-suite/Info_test.go index 00a41fea..d353d696 100644 --- a/test-suite/Info_test.go +++ b/test-suite/Info_test.go @@ -20,7 +20,7 @@ func TestInfo(t *testing.T) { case "bun": doInfo(bt, "express", "@replit/crosis", "@distube/spotify") - case "python3-poetry", "python3-pip": + case "python3-poetry", "python3-pip", "python3-uv": doInfo(bt, "Flask", "replit-ai") default: diff --git a/test-suite/Install_test.go b/test-suite/Install_test.go index 085043ce..fad5cc9f 100644 --- a/test-suite/Install_test.go +++ b/test-suite/Install_test.go @@ -15,6 +15,7 @@ var testInstalls = map[string]bool{ "nodejs-yarn": true, "python3-poetry": true, "python3-pip": true, + "python3-uv": true, } func TestInstall(t *testing.T) { diff --git a/test-suite/List_test.go b/test-suite/List_test.go index 30a59f34..d752dcdb 100644 --- a/test-suite/List_test.go +++ b/test-suite/List_test.go @@ -33,7 +33,7 @@ func TestList(t *testing.T) { }, } - case "python3-poetry": + case "python3-poetry", "python3-pip", "python3-uv": templatesToPackages = map[string][]string{ "no-deps": {}, "one-dep": {"django"}, @@ -44,16 +44,6 @@ func TestList(t *testing.T) { }, } - case "python3-pip": - templatesToPackages = map[string][]string{ - "no-deps": {}, - "one-dep": {"django"}, - "many-deps": { - "django", - "boatman", - "ws4py", - }, - } default: t.Run(bt.Backend.Name, func(t *testing.T) { t.Skip("no test") diff --git a/test-suite/Lock_test.go b/test-suite/Lock_test.go index 23f0a16a..e9c4b359 100644 --- a/test-suite/Lock_test.go +++ b/test-suite/Lock_test.go @@ -12,6 +12,7 @@ var testLocks = map[string]bool{ "nodejs-pnpm": true, "nodejs-yarn": true, "python3-poetry": true, + "python3-uv": true, } func TestLock(t *testing.T) { diff --git a/test-suite/Remove_test.go b/test-suite/Remove_test.go index 545a4cbc..527b00a9 100644 --- a/test-suite/Remove_test.go +++ b/test-suite/Remove_test.go @@ -24,13 +24,7 @@ func TestRemove(t *testing.T) { "many-deps": {"express", "eslint", "svelte"}, } - case "python3-poetry": - pkgsToRemove = map[string][]string{ - "one-dep": {"django"}, - "many-deps": {"django", "boatman", "ws4py"}, - } - - case "python3-pip": + case "python3-poetry", "python3-pip", "python3-uv": pkgsToRemove = map[string][]string{ "one-dep": {"django"}, "many-deps": {"django", "boatman", "ws4py"}, diff --git a/test-suite/Search_test.go b/test-suite/Search_test.go index bd6ae743..6cd494fd 100644 --- a/test-suite/Search_test.go +++ b/test-suite/Search_test.go @@ -24,7 +24,7 @@ func TestSearch(t *testing.T) { {"@replit", "@replit/crosis"}, }) - case "python3-poetry", "python3-pip": + case "python3-poetry", "python3-pip", "python3-uv": doSearch(bt, []searchTest{ {"flask", "Flask"}, {"replit-ai", "replit-ai"}, diff --git a/test-suite/WhichLanguage_test.go b/test-suite/WhichLanguage_test.go index 0ab0be1d..0f67bab9 100644 --- a/test-suite/WhichLanguage_test.go +++ b/test-suite/WhichLanguage_test.go @@ -13,6 +13,7 @@ var testWhichLanguage = map[string]bool{ "nodejs-yarn": true, "python3-poetry": true, "python3-pip": true, + "python3-uv": true, } func TestWhichLanguage(t *testing.T) { diff --git a/test-suite/templates/python3-pip/many-deps/requirements.txt b/test-suite/templates/python3-pip/many-deps/requirements.txt index 70ff6a34..8f2e64db 100644 --- a/test-suite/templates/python3-pip/many-deps/requirements.txt +++ b/test-suite/templates/python3-pip/many-deps/requirements.txt @@ -1,3 +1,3 @@ django>=4.2.7,<5.0.0 -boatman>=^0.0.6,<1.0.0 +boatman>=0.0.6,<1.0.0 ws4py>=0.5.1,<1.0.0 diff --git a/test-suite/templates/python3-uv/many-deps/README.md b/test-suite/templates/python3-uv/many-deps/README.md new file mode 100644 index 00000000..e69de29b diff --git a/test-suite/templates/python3-uv/many-deps/pyproject.toml b/test-suite/templates/python3-uv/many-deps/pyproject.toml new file mode 100644 index 00000000..077ba917 --- /dev/null +++ b/test-suite/templates/python3-uv/many-deps/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "python-template" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "django >=4.2.7,<5.0.0", + "boatman >=0.0.6,<1.0.0", + "ws4py >=0.5.1,<1.0.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-suite/templates/python3-uv/many-deps/src/python_template/__init__.py b/test-suite/templates/python3-uv/many-deps/src/python_template/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test-suite/templates/python3-uv/many-deps/uv.lock b/test-suite/templates/python3-uv/many-deps/uv.lock new file mode 100644 index 00000000..86e62b13 --- /dev/null +++ b/test-suite/templates/python3-uv/many-deps/uv.lock @@ -0,0 +1,97 @@ +version = 1 +requires-python = ">=3.11" + +[[package]] +name = "asgiref" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828 }, +] + +[[package]] +name = "boatman" +version = "0.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "future" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/3f/d8b24096dccd955c887c80d940cb2de333bfb9d705c60a1892d134412a7b/boatman-0.0.6.zip", hash = "sha256:fc2482032588c2c41e36b318a4ad7976b83966f86ef5f17b08ab1946589234e6", size = 562028 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/88/61e1ab09d1255477254b2c708693a8d5b5b6edf94a5ff46b2aea29ac8706/boatman-0.0.6-py2.py3-none-any.whl", hash = "sha256:8bce8527e11ecb51efb223704d1066b3b4f55939fc4f97efe889b441f8dd0fe1", size = 350904 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "django" +version = "4.2.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/1e/d6306fb0a4f44ed9d12430953786747d609c85258c74c545fdc2b37f5a83/Django-4.2.15.tar.gz", hash = "sha256:c77f926b81129493961e19c0e02188f8d07c112a1162df69bfab178ae447f94a", size = 10418066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/77/86af525feb6a9951d2bc96371ae3652f11bb35933b21966abc594f777956/Django-4.2.15-py3-none-any.whl", hash = "sha256:61ee4a130efb8c451ef3467c67ca99fdce400fedd768634efc86a68c18d80d30", size = 7992797 }, +] + +[[package]] +name = "future" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326 }, +] + +[[package]] +name = "python-template" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "boatman" }, + { name = "django" }, + { name = "ws4py" }, +] + +[package.metadata] +requires-dist = [ + { name = "boatman", specifier = ">=0.0.6,<1.0.0" }, + { name = "django", specifier = ">=4.2.7,<5.0.0" }, + { name = "ws4py", specifier = ">=0.5.1,<1.0.0" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/82/dfa23ec2cbed08a801deab02fe7c904bfb00765256b155941d789a338c68/sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e", size = 84502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a5/b2860373aa8de1e626b2bdfdd6df4355f0565b47e51f7d0c54fe70faf8fe/sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", size = 44156 }, +] + +[[package]] +name = "tzdata" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, +] + +[[package]] +name = "ws4py" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz", hash = "sha256:29d073d7f2e006373e6a848b1d00951a1107eb81f3742952be905429dc5a5483", size = 51408 } diff --git a/test-suite/templates/python3-uv/one-dep/README.md b/test-suite/templates/python3-uv/one-dep/README.md new file mode 100644 index 00000000..e69de29b diff --git a/test-suite/templates/python3-uv/one-dep/pyproject.toml b/test-suite/templates/python3-uv/one-dep/pyproject.toml new file mode 100644 index 00000000..da3ff470 --- /dev/null +++ b/test-suite/templates/python3-uv/one-dep/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "python-template" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "django>=4.2.6,<5.0.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-suite/templates/python3-uv/one-dep/src/python_template/__init__.py b/test-suite/templates/python3-uv/one-dep/src/python_template/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test-suite/templates/python3-uv/one-dep/uv.lock b/test-suite/templates/python3-uv/one-dep/uv.lock new file mode 100644 index 00000000..8703ed08 --- /dev/null +++ b/test-suite/templates/python3-uv/one-dep/uv.lock @@ -0,0 +1,54 @@ +version = 1 +requires-python = ">=3.11" + +[[package]] +name = "asgiref" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828 }, +] + +[[package]] +name = "django" +version = "4.2.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/1e/d6306fb0a4f44ed9d12430953786747d609c85258c74c545fdc2b37f5a83/Django-4.2.15.tar.gz", hash = "sha256:c77f926b81129493961e19c0e02188f8d07c112a1162df69bfab178ae447f94a", size = 10418066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/77/86af525feb6a9951d2bc96371ae3652f11bb35933b21966abc594f777956/Django-4.2.15-py3-none-any.whl", hash = "sha256:61ee4a130efb8c451ef3467c67ca99fdce400fedd768634efc86a68c18d80d30", size = 7992797 }, +] + +[[package]] +name = "python-template" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "django" }, +] + +[package.metadata] +requires-dist = [{ name = "django", specifier = ">=4.2.6,<5.0.0" }] + +[[package]] +name = "sqlparse" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/82/dfa23ec2cbed08a801deab02fe7c904bfb00765256b155941d789a338c68/sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e", size = 84502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a5/b2860373aa8de1e626b2bdfdd6df4355f0565b47e51f7d0c54fe70faf8fe/sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", size = 44156 }, +] + +[[package]] +name = "tzdata" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, +] diff --git a/test-suite/templates/templates.go b/test-suite/templates/templates.go index dec2588b..9d845b88 100644 --- a/test-suite/templates/templates.go +++ b/test-suite/templates/templates.go @@ -2,5 +2,5 @@ package templates import "embed" -//go:embed * +//go:embed all:* var FS embed.FS