Skip to content

Commit

Permalink
Fixes: #3928 ; First Test Phase
Browse files Browse the repository at this point in the history
  • Loading branch information
himanshumahajan138 committed Dec 1, 2024
1 parent e793d31 commit 231c1fd
Show file tree
Hide file tree
Showing 20 changed files with 407 additions and 5 deletions.
1 change: 1 addition & 0 deletions example/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ object `package` extends RootModule with Module {
object pythonlib extends Module {
object basic extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "basic"))
object dependencies extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "dependencies"))
object module extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "module"))
}

object cli extends Module{
Expand Down
4 changes: 2 additions & 2 deletions example/pythonlib/basic/2-custom-build-logic/foo/src/foo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@


def line_count() -> int:
with importlib.resources.open_text("resources", "line-count.txt") as file:
return int(file.readline().strip())
resource_content = (importlib.resources.files("resources").joinpath("line-count.txt").read_text())
return int(resource_content.strip())


if __name__ == "__main__":
Expand Down
75 changes: 75 additions & 0 deletions example/pythonlib/module/1-common-config/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package build
import mill._, pythonlib._

object `package` extends RootModule with PythonModule {
// You can have arbitrary numbers of third-party libraries
def pythonDeps = Seq("MarkupSafe==3.0.2", "Jinja2==3.1.4")

// choose a main Script to run if there are multiple present
def mainScript = Task.Source { millSourcePath / "custom-src" / "foo2.py" }

// TODO: we have to choose whether to include `millSourcePath` by default in PYTHONPATH or not.
// def sources = Task.Sources {
// super.sources() ++ Seq(PathRef(millSourcePath))
// }

// def resources = Task.Sources {
// super.resources() ++ Seq(PathRef(millSourcePath))
// }

def generatedSources: T[Seq[PathRef]] = Task {
val destPath = Task.dest / "generatedSources"
os.makeDir.all(destPath)
for (name <- Seq("A", "B", "C")) os.write(
destPath / s"foo$name.py",
s"""
|class Foo$name:
| value = "hello $name"
""".stripMargin
)

Seq(PathRef(destPath))
}

def forkEnv: T[Map[String, String]] = Map("MY_CUSTOM_ENV" -> "my-env-value")

// Additional Python options e.g. to Turn On Warnings
// we can use -Werror to treat warnings as errors
def pythonOptions: T[Seq[String]] = Seq("-Wall")

}

/** Usage

> ./mill run
...
Foo2.value: <h1>hello2</h1>
Foo.value: <h1>hello</h1>
FooA.value: hello A
FooB.value: hello B
FooC.value: hello C
MyResource: My Resource Contents
MyOtherResource: My Other Resource Contents
MY_CUSTOM_ENV: my-env-value
...

> ./mill show bundle
".../out/bundle.dest/bundle.pex"

> ./out/bundle.dest/bundle.pex
...
Foo2.value: <h1>hello2</h1>
Foo.value: <h1>hello</h1>
FooA.value: hello A
FooB.value: hello B
FooC.value: hello C
MyResource: My Resource Contents
MyOtherResource: My Other Resource Contents
...

> sed -i.bak 's/import os/import os, warnings; warnings.warn("This is a test warning!")/g' custom-src/foo2.py

> ./mill run
...UserWarning: This is a test warning!...

*/
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
My Other Resource Contents
48 changes: 48 additions & 0 deletions example/pythonlib/module/1-common-config/custom-src/foo2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import os
import importlib.resources
from jinja2 import Template
from foo import Foo # type: ignore
from fooA import FooA # type: ignore
from fooB import FooB # type: ignore
from fooC import FooC # type: ignore


class Foo2:

def value(self, text: str):
"""Generates an HTML template with dynamic content."""
template = Template("<h1>{{ text }}</h1>")
return template.render(text=text)

def read_resource(self, package: str, resource_name: str) -> str:
"""Reads the content of a resource file."""
try:
resource_content = (
importlib.resources.files(package).joinpath(resource_name).read_text()
)
return resource_content.strip()
except FileNotFoundError:
return f"Resource '{resource_name}' not found."

def main(self):
# Output for value()
print(f"Foo2.value: {self.value('hello2')}")
print(f"Foo.value: {Foo().value('hello')}")
print(f"FooA.value: {FooA.value}")
print(f"FooB.value: {FooB.value}")
print(f"FooC.value: {FooC.value}")

# Reading resources
print(f"MyResource: {self.read_resource('resources', 'MyResource.txt')}")
print(
f"MyOtherResource: {self.read_resource('custom-resources', 'MyOtherResources.txt')}"
)

# Accessing environment variable
my_custom_env = os.environ.get("MY_CUSTOM_ENV")
if my_custom_env:
print(f"MY_CUSTOM_ENV: {my_custom_env}")


if __name__ == "__main__":
Foo2().main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
My Resource Contents
15 changes: 15 additions & 0 deletions example/pythonlib/module/1-common-config/src/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from jinja2 import Template


class Foo:
def value(self, text: str):
"""Generates an HTML template with dynamic content."""
template = Template("<h1>{{ text }}</h1>")
return template.render(text=text)

def main(self):
print(f"Foo.value: {self.value('hello')}")


if __name__ == "__main__":
Foo().main()
57 changes: 57 additions & 0 deletions example/pythonlib/module/2-custom-tasks/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package build
import mill._, pythonlib._

object `package` extends RootModule with PythonModule {

def pythonDeps = Seq("argparse==1.4.0", "jinja2==3.1.4")

def mainScript = Task.Source { millSourcePath / "src" / "foo.py" }

def generatedSources: T[Seq[PathRef]] = Task {
val destPath = Task.dest / "generatedSources"
os.makeDir.all(destPath)

val prettyPythonDeps = pythonDeps().map { dep =>
val parts = dep.split("==")
s"""("${parts(0)}", "${parts(1)}")"""
}.mkString(", ")

os.write(
destPath / s"myDeps.py",
s"""
|class MyDeps:
| value = [${prettyPythonDeps}]
""".stripMargin
)

Seq(PathRef(destPath))
}

def lineCount: T[Int] = Task {
sources()
.flatMap(pathRef => os.walk(pathRef.path))
.filter(_.ext == "py")
.map(os.read.lines(_).size)
.sum
}

def forkEnv: T[Map[String, String]] = Map("MY_LINE_COUNT" -> s"${lineCount()}")

def printLineCount() = Task.Command { println(lineCount()) }

}

/** Usage

> ./mill run --text hello
text: hello
MyDeps.value: [('argparse', '1.4.0'), ('jinja2', '3.1.4')]
My_Line_Count: 22

> ./mill show lineCount
22

> ./mill printLineCount
22

*/
22 changes: 22 additions & 0 deletions example/pythonlib/module/2-custom-tasks/src/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import argparse
import os
from myDeps import MyDeps # type: ignore


class Foo:
def main(self, text: str) -> None:
print("text: ", text)
print("MyDeps.value: ", MyDeps.value)
print("My_Line_Count: ", os.environ.get("MY_LINE_COUNT"))

if __name__ == '__main__':
# Create the argument parser
parser = argparse.ArgumentParser(description="Process text argument")

# Add argument for text
parser.add_argument("--text", type=str, required=True, help="Text for printing")

# Parse the arguments
args = parser.parse_args()

Foo().main(args.text)
68 changes: 68 additions & 0 deletions example/pythonlib/module/3-override-tasks/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package build
import mill._, pythonlib._

object foo extends PythonModule {
def sources = Task.Sources {
val destPath = Task.dest / "src"
os.makeDir.all(destPath)

os.write(
destPath / "foo.py",
s"""
|class Foo:
| def main(self) -> None:
| print("Hello World")
|
|if __name__ == '__main__':
| Foo().main()
""".stripMargin
)
Seq(PathRef(destPath))
}

def mainScript = Task.Source { sources().head.path / "foo.py" }

def typeCheck = Task {
println("Type Checking...")
super.typeCheck()
}

def run(args: mill.define.Args) = Task.Command {
typeCheck()
println("Running..." + args.value.mkString(" "))
super.run(args)()
}
}

object foo2 extends PythonModule {
def generatedSources = Task {
val destPath = Task.dest / "src"
os.makeDir.all(destPath)
os.write(destPath / "foo.py", """...""")
Seq(PathRef(destPath))
}

def mainScript = Task.Source { generatedSources().head.path / "foo.py" }

}

object foo3 extends PythonModule {
def sources = Task {
val destPath = Task.dest / "src"
os.makeDir.all(destPath)
os.write(destPath / "foo.py", """...""")
super.sources() ++ Seq(PathRef(destPath))
}
def mainScript = Task.Source { sources().head.path / "foo.py" }

}

/** Usage

> ./mill foo.run
Type Checking...
Success: no issues found in 1 source file
Running...
Hello World

*/
15 changes: 15 additions & 0 deletions example/pythonlib/module/4-compilation-execution-flags/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package build
import mill._, pythonlib._

object `package` extends RootModule with PythonModule {
def mainScript = Task.Source { millSourcePath / "src" / "foo.py" }
def pythonOptions = Seq("-Wall", "-Xdev")
def forkEnv = Map("MY_ENV_VAR" -> "HELLO MILL!")
}

/** Usage

> ./mill run
HELLO MILL!

*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

class Foo:
def main(self) -> None:
print(os.environ.get("MY_ENV_VAR"))

if __name__ == '__main__':
Foo().main()
29 changes: 29 additions & 0 deletions example/pythonlib/module/5-resources/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package build
import mill._, pythonlib._

object foo extends PythonModule {

def mainScript = Task.Source { millSourcePath / "src" / "foo.py" }

object test extends PythonTests with TestModule.Unittest {
def otherFiles = Task.Source(millSourcePath / "other-files")

def forkEnv: T[Map[String, String]] =
super.forkEnv() ++ Map("OTHER_FILES_DIR" -> otherFiles().path.toString)
}
}

/** Usage

> ./mill foo.run
Hello World Resource File

> ./mill foo.test
...
test_all (test.TestScript.test_all) ... ok
...Ran 1 test...
...
OK
...

*/
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World Resource File
11 changes: 11 additions & 0 deletions example/pythonlib/module/5-resources/foo/src/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import importlib.resources

class Foo:
def PythonPathResourceText(self, package, resourceName: str) -> None:
resource_content = (
importlib.resources.files(package).joinpath(resourceName).read_text()
)
return resource_content.strip()

if __name__ == "__main__":
print(Foo().PythonPathResourceText("resources", "file.txt"))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Other Hello World File
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Hello World Resource File A
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Hello World Resource File B
Loading

0 comments on commit 231c1fd

Please sign in to comment.