-
-
Notifications
You must be signed in to change notification settings - Fork 390
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Some improvements to pythonlib (#3992)
This is in view of #3928, specifically some essential things required for the first task, `example/pythonlib/basic/`. You can look at commits individually if you like. The gist of this PR is to improve the scaffolding we currently have to unblock the previously mentioned tasks. It can be summarized by the following points (roughly each corresponding to one commit): - [x] make `run` task the default, following the same convention used in `ScalaModule` and `JavaModule` - [x] add a command to run an interactive REPL. This is named `console`, again following the conventions of scalalib - [x] rework the way source files are handled: - support multiple source directories, again following similar conventions of scalalib - instead of aggreting python scripts in one syntetic directory, keep them where they are and instead manipulate PYTHONPATH or pass in parameters to various directories where appropriate - [x] define a module for unit tests, similar to the TestModule of scalalib
- Loading branch information
Showing
10 changed files
with
294 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
#!/usr/bin/python3 | ||
import numpy as np | ||
|
||
from foo.src.foo import data | ||
from foo.bar.src.bar import df | ||
from foo import data | ||
from bar import df | ||
|
||
def main() -> None: | ||
print(f"Numpy : Sum: {np.sum(data)} | Pandas: Mean: {df['Values'].mean()}, Max: {df['Values'].max()}") | ||
|
||
if __name__ == "__main__": | ||
main() | ||
main() |
17 changes: 17 additions & 0 deletions
17
example/pythonlib/basic/1-simple/qux/test/src/test_dummy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import unittest | ||
|
||
class TestStringMethods(unittest.TestCase): | ||
|
||
def test_upper(self): | ||
self.assertEqual('foo'.upper(), 'FOO') | ||
|
||
def test_isupper(self): | ||
self.assertTrue('FOO'.isupper()) | ||
self.assertFalse('Foo'.isupper()) | ||
|
||
def test_split(self): | ||
s = 'hello world' | ||
self.assertEqual(s.split(), ['hello', 'world']) | ||
# check that s.split fails when the separator is not a string | ||
with self.assertRaises(TypeError): | ||
s.split(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package mill.pythonlib | ||
|
||
import mill.Task | ||
import mill.Command | ||
import mill.TaskModule | ||
import mill.T | ||
|
||
trait TestModule extends TaskModule { | ||
import TestModule.TestResult | ||
|
||
/** | ||
* Discovers and runs the module's tests in a subprocess, reporting the | ||
* results to the console. | ||
* @see [[testCached]] | ||
*/ | ||
def test(args: String*): Command[Seq[TestResult]] = | ||
Task.Command { | ||
testTask(Task.Anon { args })() | ||
} | ||
|
||
/** | ||
* Args to be used by [[testCached]]. | ||
*/ | ||
def testCachedArgs: T[Seq[String]] = Task { Seq[String]() } | ||
|
||
/** | ||
* Discovers and runs the module's tests in a subprocess, reporting the | ||
* results to the console. | ||
* If no input has changed since the last run, no test were executed. | ||
* @see [[test()]] | ||
*/ | ||
def testCached: T[Seq[TestResult]] = Task { | ||
testTask(testCachedArgs)() | ||
} | ||
|
||
/** | ||
* The actual task shared by `test`-tasks. | ||
*/ | ||
protected def testTask(args: Task[Seq[String]]): Task[Seq[TestResult]] | ||
|
||
override def defaultCommandName() = "test" | ||
} | ||
|
||
object TestModule { | ||
|
||
// TODO: this is a dummy for now, however we should look into re-using | ||
// mill.testrunner.TestResults | ||
type TestResult = Unit | ||
|
||
/** TestModule that uses Python's standard unittest module to run tests. */ | ||
trait Unittest extends PythonModule with TestModule { | ||
protected def testTask(args: Task[Seq[String]]) = Task.Anon { | ||
val testArgs = if (args().isEmpty) { | ||
Seq("discover") ++ sources().flatMap(pr => Seq("-s", pr.path.toString)) | ||
} else { | ||
args() | ||
} | ||
runner().run( | ||
("-m", "unittest", testArgs) | ||
) | ||
Seq() | ||
} | ||
} | ||
|
||
/** TestModule that uses pytest to run tests. */ | ||
trait Pytest extends PythonModule with TestModule { | ||
|
||
override def pythonDeps: T[Seq[String]] = T { | ||
super.pythonDeps() ++ Seq("pytest==8.3.3") | ||
} | ||
|
||
protected def testTask(args: Task[Seq[String]]) = Task.Anon { | ||
runner().run( | ||
( | ||
// format: off | ||
"-m", "pytest", | ||
"-o", s"cache_dir=${Task.dest / "cache"}", | ||
sources().map(_.path), | ||
args() | ||
// format: in | ||
) | ||
) | ||
Seq() | ||
} | ||
} | ||
|
||
} |
5 changes: 4 additions & 1 deletion
5
pythonlib/test/resources/hello-world-python/foo/bar/src/bar.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
import sys | ||
|
||
bar_val: int = 42 | ||
|
||
def main() -> None: print("Hello, " + " ".join(sys.argv[1:]) + " Foo Bar!") | ||
if __name__ == "__main__": | ||
main() | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
import sys | ||
import bar | ||
|
||
foo_val: int = bar.bar_val | ||
|
||
def main() -> None: print("Hello, " + " ".join(sys.argv[1:]) + " Foo!") | ||
if __name__ == "__main__": | ||
main() | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
import sys | ||
import foo | ||
|
||
qux_val: int = foo.foo_val | ||
|
||
def main() -> None: print("Hello, " + " ".join(sys.argv[1:]) + " Qux!") | ||
if __name__ == "__main__": | ||
main() | ||
main() |
Oops, something went wrong.