Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stdlib] Implement mkdir and rmdir #2430

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion stdlib/src/os/__init__.mojo
Expand Up @@ -15,5 +15,15 @@
from .atomic import Atomic
from .env import setenv, getenv
from .fstat import lstat, stat, stat_result
from .os import abort, listdir, remove, unlink, SEEK_SET, SEEK_CUR, SEEK_END
from .os import (
abort,
listdir,
remove,
unlink,
SEEK_SET,
SEEK_CUR,
SEEK_END,
mkdir,
rmdir,
)
from .pathlike import PathLike
59 changes: 58 additions & 1 deletion stdlib/src/os/os.mojo
Expand Up @@ -277,7 +277,7 @@ fn remove(path: String) raises:
path: The path to the file.

"""
var error = external_call["unlink", Int](path._as_ptr())
var error = external_call["unlink", Int32](path._as_ptr())

if error != 0:
# TODO get error message, the following code prints it
Expand Down Expand Up @@ -327,3 +327,60 @@ fn unlink[pathlike: os.PathLike](path: pathlike) raises:

"""
remove(path.__fspath__())


# ===----------------------------------------------------------------------=== #
# mkdir/rmdir
# ===----------------------------------------------------------------------=== #


fn mkdir(path: String, mode: Int = 0o777) raises:
"""Creates a directory at the specified path.
If the directory can not be created an error is raised.
Absolute and relative paths are allowed, relative paths are resolved from cwd.

Args:
path: The path to the directory.
mode: The mode to create the directory with.
"""

var error = external_call["mkdir", Int32](path._as_ptr(), mode)
if error != 0:
raise Error("Can not create directory: " + path)


fn mkdir[pathlike: os.PathLike](path: pathlike, mode: Int = 0o777) raises:
"""Creates a directory at the specified path.
If the directory can not be created an error is raised.
Absolute and relative paths are allowed, relative paths are resolved from cwd.

Args:
path: The path to the directory.
mode: The mode to create the directory with.
"""

mkdir(path.__fspath__(), mode)


fn rmdir(path: String) raises:
"""Removes the specified directory.
If the path is not a directory or it can not be deleted, an error is raised.
Absolute and relative paths are allowed, relative paths are resolved from cwd.

Args:
path: The path to the directory.
"""
var error = external_call["rmdir", Int32](path._as_ptr())
if error != 0:
raise Error("Can not remove directory: " + path)


fn rmdir[pathlike: os.PathLike](path: pathlike) raises:
"""Removes the specified directory.
If the path is not a directory or it can not be deleted, an error is raised.
Absolute and relative paths are allowed, relative paths are resolved from cwd.

Args:
path: The path to the directory.
"""
rmdir(path.__fspath__())
121 changes: 121 additions & 0 deletions stdlib/test/os/test_mkdir_and_rmdir.mojo
@@ -0,0 +1,121 @@
# ===----------------------------------------------------------------------=== #
# Copyright (c) 2024, Modular Inc. All rights reserved.
#
# Licensed under the Apache License v2.0 with LLVM Exceptions:
# https://llvm.org/LICENSE.txt
#
# 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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %s

from os import mkdir, rmdir, remove
from os.path import exists
from pathlib import Path

from testing import assert_true, assert_false, assert_raises


fn create_dir_and_test_delete_string[
func_create: fn (String, Int) raises -> None,
func_delete: fn (String) raises -> None,
](dir_name: String) raises:
# verify that the test dir does not exist before starting the test
assert_false(
exists(dir_name),
"Unexpected dir " + dir_name + " it should not exist",
)

func_create(dir_name, 0o777)
assert_true(exists(dir_name))

func_delete(dir_name)
# trying to delete non existing dir
with assert_raises(contains="Can not remove directory: "):
func_delete(dir_name)


fn create_dir_and_test_delete_path[
func_create: fn[pathlike: PathLike] (pathlike, Int) raises -> None,
func_delete: fn[pathlike: PathLike] (pathlike) raises -> None,
](dir_path: Path) raises:
# verify that the test dir does not exist before starting the test
assert_false(
exists(dir_path),
"Unexpected dir " + dir_path.__fspath__() + " it should not exist",
)

func_create(dir_path, 0o777)
assert_true(exists(dir_path))

func_delete(dir_path)
# trying to delete non existing dir
with assert_raises(contains="Can not remove directory: "):
func_delete(dir_path)


fn test_mkdir_and_rmdir() raises:
var cwd_path = Path()
var my_dir_path = cwd_path / "my_dir"
var my_dir_name = str(my_dir_path)

create_dir_and_test_delete_path[mkdir, rmdir](my_dir_path)
create_dir_and_test_delete_string[mkdir, rmdir](my_dir_name)

# test relative path
create_dir_and_test_delete_string[mkdir, rmdir]("my_relative_dir")
create_dir_and_test_delete_path[mkdir, rmdir](Path("my_relative_dir"))


fn test_mkdir_mode() raises:
var cwd_path = Path()
var my_dir_path = cwd_path / "my_dir"
var file_name = my_dir_path / "file.txt"

assert_false(
exists(my_dir_path),
"Unexpected dir " + my_dir_path.__fspath__() + " it should not exist",
)

# creating dir without writing permission
mkdir(my_dir_path, 0o111)

with assert_raises(contains="Permission denied"):
var file = open(file_name, "w")
file.close()
if exists(file_name):
remove(file_name)

if exists(my_dir_path):
rmdir(my_dir_path)


fn test_rmdir_not_emty() raises:
var cwd_path = Path()
var my_dir_path = cwd_path / "my_dir"
var file_name = my_dir_path / "file.txt"

assert_false(
exists(my_dir_path),
"Unexpected dir " + my_dir_path.__fspath__() + " it should not exist",
)

mkdir(my_dir_path)
with open(file_name, "w"):
pass

with assert_raises(contains="Can not remove directory: "):
rmdir(my_dir_path)

remove(file_name)
rmdir(my_dir_path)
assert_false(exists(my_dir_path), "Failed to remove dir")


fn main() raises:
test_mkdir_and_rmdir()
test_mkdir_mode()
test_rmdir_not_emty()