mirror of
https://github.com/facebook/sapling.git
synced 2024-10-04 22:07:44 +03:00
suppress errors in eden
- batch 1
Reviewed By: pradeep90 Differential Revision: D22628389 fbshipit-source-id: c109334243a92269a7fb9a9b0515565ac22ecfac
This commit is contained in:
parent
209c054215
commit
6527db2809
@ -340,7 +340,7 @@ class EdenInstance:
|
||||
data: Dict[str, Mapping[str, str]] = {}
|
||||
for section in parser.sections():
|
||||
data[section] = parser.get_section_str_to_any(section)
|
||||
toml.dump(data, file) # pyre-ignore[T39129461]
|
||||
toml.dump(data, file)
|
||||
|
||||
def get_mount_paths(self) -> List[str]:
|
||||
"""Return the paths of the set mount points stored in config.json"""
|
||||
|
@ -116,8 +116,6 @@ class OverlayCmd(subcmd_mod.Subcmd):
|
||||
)
|
||||
|
||||
def _print_file(self, f: BinaryIO) -> None:
|
||||
# pyre-fixme[6]: Expected `_Reader[Variable[typing.AnyStr <: [str, bytes]]]`
|
||||
# for 1st param but got `BinaryIO`.
|
||||
shutil.copyfileobj(f, sys.stdout.buffer)
|
||||
|
||||
def _process_overlay(self, inode_number: int, path: Path, level: int = 0) -> None:
|
||||
|
@ -878,7 +878,6 @@ https://fb.facebook.com/groups/eden.users/
|
||||
self.assertEqual(1, exit_code)
|
||||
|
||||
def test_remount_checkouts(self) -> None:
|
||||
# pyre-ignore[20]: T31414181
|
||||
exit_code, out, mounts = self._test_remount_checkouts(dry_run=False)
|
||||
self.assertEqual(
|
||||
f"""\
|
||||
@ -895,7 +894,6 @@ Remounting {mounts[1]}...<green>fixed<reset>
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
def test_remount_checkouts_old_edenfs(self) -> None:
|
||||
# pyre-ignore[20]: T31414181
|
||||
exit_code, out, mounts = self._test_remount_checkouts(
|
||||
dry_run=False, old_edenfs=True
|
||||
)
|
||||
@ -914,7 +912,6 @@ Remounting {mounts[1]}...<green>fixed<reset>
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
def test_remount_checkouts_dry_run(self) -> None:
|
||||
# pyre-ignore[20]: T31414181
|
||||
exit_code, out, mounts = self._test_remount_checkouts(
|
||||
dry_run=True, old_edenfs=True
|
||||
)
|
||||
@ -933,6 +930,8 @@ Would remount {mounts[1]}
|
||||
self.assertEqual(exit_code, 1)
|
||||
|
||||
@patch("eden.fs.cli.doctor.check_watchman._call_watchman")
|
||||
# pyre-fixme[56]: Argument `set()` to decorator factory `unittest.mock.patch`
|
||||
# could not be resolved in a global scope.
|
||||
@patch(
|
||||
"eden.fs.cli.doctor.check_watchman._get_roots_for_nuclide", return_value=set()
|
||||
)
|
||||
|
@ -60,6 +60,7 @@ def setup_hg_dir(checkout: EdenCheckout, commit_id: str) -> None:
|
||||
(checkout_hg_dir / "branch").write_text("default\n")
|
||||
|
||||
# Write the parents to the dirstate file.
|
||||
# pyre-fixme[22]: The cast is redundant.
|
||||
with typing.cast(BinaryIO, (checkout_hg_dir / "dirstate").open("wb")) as f:
|
||||
parents = (binascii.unhexlify(commit_id), b"\x00" * 20)
|
||||
tuples_dict: Dict[str, Tuple[str, int, int]] = {}
|
||||
|
@ -321,8 +321,6 @@ class Overlay:
|
||||
os.symlink(contents, bytes(output_path))
|
||||
elif file_type == stat.S_IFREG:
|
||||
with output_path.open("wb") as outf:
|
||||
# pyre-fixme[6]: Expected `_Reader[Variable[typing.AnyStr <:
|
||||
# [str, bytes]]]` for 1st param but got `BinaryIO`.
|
||||
shutil.copyfileobj(inf, outf)
|
||||
# Note: the file permissions bits are now stored in the inode table
|
||||
# rather than the overlay. The mode bits in the overlay will
|
||||
|
@ -275,21 +275,33 @@ class TestTelemetryLogger(BaseJsonTelemetryLogger):
|
||||
|
||||
class NullTelemetrySample(TelemetrySample):
|
||||
def add_int(self, name: str, value: int) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def add_string(self, name: str, value: str) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def add_double(self, name: str, value: float) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def add_tags(self, name: str, value: Set[str]) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def add_bool(self, name: str, value: bool) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def add_fields(self, **kwargs: _TelemetryTypes) -> "NullTelemetrySample":
|
||||
# pyre-fixme[7]: Expected `NullTelemetrySample` but got implicit return
|
||||
# value of `None`.
|
||||
pass
|
||||
|
||||
def _log_impl(self) -> None:
|
||||
|
@ -458,7 +458,6 @@ class EdenConfigParserTest(unittest.TestCase):
|
||||
parser.read_dict({"test_section": {"a": "a value"}})
|
||||
with self.assertRaises(configparser.NoSectionError) as expectation:
|
||||
parser.get_section_str_to_str("not_test_section")
|
||||
# pyre-fixme[16]: `NoSectionError` has no attribute `section`.
|
||||
section: str = expectation.exception.section
|
||||
self.assertEqual(section, "not_test_section")
|
||||
|
||||
|
@ -587,7 +587,6 @@ def split_inodes_by_operation_type(
|
||||
file_size=n.fileSize,
|
||||
)
|
||||
for tree in inode_results
|
||||
# pyre-fixme[10]: Name `tree` is used but not defined.
|
||||
for n in tree.entries
|
||||
if n.loaded and stat.S_IFMT(n.mode) == stat.S_IFREG
|
||||
]
|
||||
|
@ -256,7 +256,7 @@ class CloneTest(testcase.EdenRepoTest):
|
||||
clone_output = self.eden.run_cmd(
|
||||
"clone",
|
||||
"--daemon-binary",
|
||||
FindExe.EDEN_DAEMON, # pyre-ignore[6]: T38947910
|
||||
FindExe.EDEN_DAEMON,
|
||||
self.repo.path,
|
||||
str(tmp),
|
||||
"--daemon-args",
|
||||
@ -313,8 +313,8 @@ class CloneFakeEdenFSTestBase(ServiceTestCaseBase):
|
||||
mount_path: Path,
|
||||
extra_args: Optional[Sequence[str]] = None,
|
||||
) -> subprocess.CompletedProcess:
|
||||
eden_cli: str = FindExe.EDEN_CLI # pyre-ignore[9]: T38947910
|
||||
fake_edenfs: str = FindExe.FAKE_EDENFS # pyre-ignore[9]: T38947910
|
||||
eden_cli: str = FindExe.EDEN_CLI
|
||||
fake_edenfs: str = FindExe.FAKE_EDENFS
|
||||
base_args = [
|
||||
eden_cli,
|
||||
"--config-dir",
|
||||
|
@ -456,6 +456,8 @@ class UpdateTest(EdenHgTestCase):
|
||||
p1.join()
|
||||
p2.join()
|
||||
|
||||
# pyre-fixme[56]: Argument `"SANDCASTLE" in os.environ` to decorator factory
|
||||
# `unittest.skipIf` could not be resolved in a global scope.
|
||||
@unittest.skipIf(
|
||||
"SANDCASTLE" in os.environ,
|
||||
"This test seems to leave behind unkillable processes "
|
||||
|
@ -235,7 +235,7 @@ class EdenFS(object):
|
||||
)
|
||||
|
||||
def get_extra_daemon_args(self) -> List[str]:
|
||||
extra_daemon_args: List[str] = [ # pyre-ignore[9]: T38947910
|
||||
extra_daemon_args: List[str] = [
|
||||
# Defaulting to 8 import processes is excessive when the test
|
||||
# framework runs tests on each CPU core.
|
||||
"--num_hg_import_threads",
|
||||
@ -277,10 +277,7 @@ class EdenFS(object):
|
||||
raise Exception("cannot start an already-running eden client")
|
||||
|
||||
args = self.get_eden_cli_args(
|
||||
"daemon",
|
||||
"--daemon-binary",
|
||||
FindExe.EDEN_DAEMON, # pyre-ignore[6]: T38947910
|
||||
"--foreground",
|
||||
"daemon", "--daemon-binary", FindExe.EDEN_DAEMON, "--foreground"
|
||||
)
|
||||
|
||||
extra_daemon_args = self.get_extra_daemon_args()
|
||||
@ -399,7 +396,6 @@ class EdenFS(object):
|
||||
daemon. Attempts to access files or directories inside the mount will fail with
|
||||
an ENOTCONN error after this.
|
||||
"""
|
||||
# pyre-ignore[9]: T38947910
|
||||
cmd: List[str] = [FindExe.TAKEOVER_TOOL, "--edenDir", str(self._eden_dir)]
|
||||
self.run_takeover_tool(cmd)
|
||||
|
||||
@ -409,7 +405,6 @@ class EdenFS(object):
|
||||
output is as expected. Right now, this is used as a sanity check to
|
||||
make sure we don't crash.
|
||||
"""
|
||||
# pyre-ignore[9]: T38947910
|
||||
cmd: List[str] = [
|
||||
FindExe.TAKEOVER_TOOL,
|
||||
"--edenDir",
|
||||
@ -426,7 +421,6 @@ class EdenFS(object):
|
||||
so the subprocess call will throw, and we expect the old process
|
||||
to recover
|
||||
"""
|
||||
# pyre-ignore[9]: T38947910
|
||||
cmd: List[str] = [
|
||||
FindExe.TAKEOVER_TOOL,
|
||||
"--edenDir",
|
||||
|
@ -27,7 +27,7 @@ class FakeEdenFS(typing.ContextManager[int]):
|
||||
home_dir: pathlib.Path,
|
||||
extra_arguments: typing.Optional[typing.Sequence[str]] = None,
|
||||
) -> "FakeEdenFS":
|
||||
command: typing.List[str] = [ # pyre-ignore[9]: T38947910
|
||||
command: typing.List[str] = [
|
||||
FindExe.FAKE_EDENFS,
|
||||
"--configPath",
|
||||
str(home_dir / ".edenrc"),
|
||||
@ -50,7 +50,7 @@ class FakeEdenFS(typing.ContextManager[int]):
|
||||
home_dir: pathlib.Path,
|
||||
extra_arguments: typing.Optional[typing.Sequence[str]] = None,
|
||||
) -> "FakeEdenFS":
|
||||
command: typing.List[str] = [ # pyre-ignore[9]: T38947910
|
||||
command: typing.List[str] = [
|
||||
FindExe.EDEN_CLI,
|
||||
"--config-dir",
|
||||
str(eden_dir),
|
||||
|
@ -185,7 +185,7 @@ class FindExeClass(object):
|
||||
return hg_real_bin
|
||||
|
||||
# Fall back to the hg.real binary
|
||||
return self.HG_REAL # pyre-ignore[7]: T38947910
|
||||
return self.HG_REAL
|
||||
|
||||
def _find_hg_real(self) -> str:
|
||||
hg_real_bin = self._find_exe_optional(
|
||||
|
@ -28,7 +28,7 @@ class GitRepository(repobase.Repository):
|
||||
|
||||
def __init__(self, path: str, temp_mgr: Optional[TempFileManager] = None) -> None:
|
||||
super().__init__(path)
|
||||
self.git_bin = FindExe.GIT # pyre-ignore[8]: T38947910
|
||||
self.git_bin = FindExe.GIT
|
||||
self.temp_mgr = temp_mgr or TempFileManager("gitrepo")
|
||||
|
||||
def git(
|
||||
|
@ -50,7 +50,6 @@ class HgRepository(repobase.Repository):
|
||||
k: v for k, v in os.environ.items() if not k.startswith("HG")
|
||||
}
|
||||
self.hg_environment["HGPLAIN"] = "1"
|
||||
# pyre-ignore[6]: T38947910
|
||||
self.hg_environment["HG_REAL_BIN"] = FindExe.HG_REAL
|
||||
self.hg_environment["NOSCMLOG"] = "1"
|
||||
# Set HGRCPATH to make sure we aren't affected by the local system's
|
||||
@ -59,7 +58,7 @@ class HgRepository(repobase.Repository):
|
||||
self.hg_environment["HGRCPATH"] = system_hgrc
|
||||
else:
|
||||
self.hg_environment["HGRCPATH"] = ""
|
||||
self.hg_bin = FindExe.HG # pyre-ignore[8]: T38947910
|
||||
self.hg_bin = FindExe.HG
|
||||
|
||||
@classmethod
|
||||
def get_system_hgrc_contents(cls) -> str:
|
||||
|
@ -109,6 +109,9 @@ class Repository(object):
|
||||
contents = contents.encode()
|
||||
|
||||
with open(full_path, "wb") as f:
|
||||
# pyre-fixme[6]: Expected `Union[array.array[typing.Any], bytearray,
|
||||
# bytes, memoryview, mmap.mmap]` for 1st param but got `Union[bytes,
|
||||
# Variable[AnyStr <: [str, bytes]]]`.
|
||||
f.write(contents)
|
||||
|
||||
os.chmod(full_path, mode)
|
||||
|
@ -161,7 +161,6 @@ class SystemdServiceTest(ServiceTestCaseBase):
|
||||
systemd = self.make_temporary_systemd_user_service_manager()
|
||||
self.systemd = systemd
|
||||
systemd.enable_runtime_unit_from_file(
|
||||
# pyre-ignore[6]: T38947910
|
||||
unit_file=pathlib.Path(FindExe.SYSTEMD_FB_EDENFS_SERVICE)
|
||||
)
|
||||
for name, value in systemd.extra_env.items():
|
||||
|
@ -413,7 +413,6 @@ def _transient_managed_systemd_user_service_manager(
|
||||
"""Create an isolated systemd instance using 'systemd-run systemd'."""
|
||||
|
||||
child_systemd_service = parent_systemd.systemd_run(
|
||||
# pyre-ignore[6]: T38947910
|
||||
command=[FindExe.SYSTEMD, "--user", "--unit=basic.target"],
|
||||
properties={
|
||||
"Description": f"Eden test systemd user service manager "
|
||||
@ -483,11 +482,9 @@ class _TransientUnmanagedSystemdUserServiceManager:
|
||||
# HACK(strager): Work around 'systemd --user' refusing to start if the
|
||||
# system is not managed by systemd.
|
||||
env["LD_PRELOAD"] = str(
|
||||
# pyre-ignore[6]: T38947910
|
||||
pathlib.Path(FindExe.FORCE_SD_BOOTED).resolve(strict=True)
|
||||
)
|
||||
process = subprocess.Popen(
|
||||
# pyre-ignore[6]: T38947910
|
||||
[
|
||||
"timeout",
|
||||
f"{self.__lifetime_duration}s",
|
||||
|
@ -62,6 +62,9 @@ class LinuxCgroupTest(IntegrationTestCase):
|
||||
),
|
||||
)
|
||||
|
||||
# pyre-fixme[56]: Argument `not
|
||||
# eden.integration.lib.linux.is_cgroup_v2_mounted()` to decorator factory
|
||||
# `unittest.skipIf` could not be resolved in a global scope.
|
||||
@unittest.skipIf(
|
||||
not is_cgroup_v2_mounted(),
|
||||
"T36934106: Fix EdenFS systemd integration tests for cgroups v1",
|
||||
|
@ -120,6 +120,7 @@ def update_ownership(metadata_path: Path, uid: int, gid: int) -> None:
|
||||
"""Update an Eden inode metadata table file, replacing the UID and GID fields
|
||||
for all inodes with the specified values.
|
||||
"""
|
||||
# pyre-fixme[22]: The cast is redundant.
|
||||
with typing.cast(BinaryIO, metadata_path.open("rb")) as input_file:
|
||||
header = MdvHeader.read(input_file)
|
||||
|
||||
@ -161,7 +162,6 @@ def update_ownership(metadata_path: Path, uid: int, gid: int) -> None:
|
||||
pass
|
||||
raise
|
||||
finally:
|
||||
# pyre-fixme[25]: Assertion will always fail.
|
||||
if tmp_file is not None:
|
||||
tmp_file.close()
|
||||
|
||||
|
@ -124,6 +124,8 @@ class ExpectedFileSet(Mapping[Path, ExpectedFileBase]):
|
||||
key = Path(path)
|
||||
del self._entries[key]
|
||||
|
||||
# pyre-fixme[14]: `__contains__` overrides method defined in `Mapping`
|
||||
# inconsistently.
|
||||
def __contains__(self, path: object) -> bool:
|
||||
if isinstance(path, str):
|
||||
key = Path(path)
|
||||
|
@ -136,7 +136,7 @@ class StartWithRepoTest(testcase.EdenRepoTest):
|
||||
class DirectInvokeTest(testcase.IntegrationTestCase):
|
||||
def test_eden_cmd_arg(self) -> None:
|
||||
"""Directly invoking edenfs with an edenfsctl subcommand should fail."""
|
||||
cmd: List[str] = [FindExe.EDEN_DAEMON, "restart"] # pyre-ignore[9]: T38947910
|
||||
cmd: List[str] = [FindExe.EDEN_DAEMON, "restart"]
|
||||
out = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
self.assertNotEqual(out.returncode, 0)
|
||||
self.assertEqual(b"", out.stdout)
|
||||
@ -168,7 +168,7 @@ class StartFakeEdenFSTestBase(ServiceTestCaseBase):
|
||||
eden_dir: Optional[pathlib.Path] = None,
|
||||
extra_args: Optional[Sequence[str]] = None,
|
||||
) -> subprocess.CompletedProcess:
|
||||
start_cmd = self._get_base_eden_args(eden_dir) + [ # pyre-ignore[6]: T38947910
|
||||
start_cmd = self._get_base_eden_args(eden_dir) + [
|
||||
"start",
|
||||
"--daemon-binary",
|
||||
FindExe.FAKE_EDENFS,
|
||||
@ -197,11 +197,7 @@ class StartFakeEdenFSTestBase(ServiceTestCaseBase):
|
||||
extra_args: Optional[Sequence[str]] = None,
|
||||
) -> None:
|
||||
base_args = self._get_base_eden_args(eden_dir)
|
||||
start_cmd = base_args + [ # pyre-ignore[6]: T38947910
|
||||
"start",
|
||||
"--daemon-binary",
|
||||
FindExe.FAKE_EDENFS,
|
||||
]
|
||||
start_cmd = base_args + ["start", "--daemon-binary", FindExe.FAKE_EDENFS]
|
||||
if extra_args:
|
||||
start_cmd.extend(extra_args)
|
||||
subprocess.check_call(start_cmd)
|
||||
@ -324,8 +320,8 @@ class StartFakeEdenFSTest(StartFakeEdenFSTestBase):
|
||||
config_dir_args = ["--config-dir", str(input_path)]
|
||||
else:
|
||||
config_dir_args = []
|
||||
eden_cli: str = FindExe.EDEN_CLI # pyre-ignore[9]: T38947910
|
||||
fake_edenfs: str = FindExe.FAKE_EDENFS # pyre-ignore[9]: T38947910
|
||||
eden_cli: str = FindExe.EDEN_CLI
|
||||
fake_edenfs: str = FindExe.FAKE_EDENFS
|
||||
base_args = [eden_cli] + self.get_required_eden_cli_args() + config_dir_args
|
||||
start_cmd = base_args + ["start", "--daemon-binary", fake_edenfs]
|
||||
stop_cmd = base_args + ["stop"]
|
||||
@ -391,7 +387,7 @@ def run_eden_start_with_real_daemon(
|
||||
env["EDEN_EXPERIMENTAL_SYSTEMD"] = "1"
|
||||
else:
|
||||
env.pop("EDEN_EXPERIMENTAL_SYSTEMD", None)
|
||||
eden_cli_args: List[str] = [ # pyre-ignore[9]: T38947910
|
||||
eden_cli_args: List[str] = [
|
||||
FindExe.EDEN_CLI,
|
||||
"--config-dir",
|
||||
str(eden_dir),
|
||||
@ -401,7 +397,7 @@ def run_eden_start_with_real_daemon(
|
||||
str(home_dir),
|
||||
]
|
||||
|
||||
start_cmd: List[str] = eden_cli_args + [ # pyre-ignore[6]: T38947910
|
||||
start_cmd: List[str] = eden_cli_args + [
|
||||
"start",
|
||||
"--daemon-binary",
|
||||
FindExe.EDEN_DAEMON,
|
||||
|
@ -299,6 +299,9 @@ ExecStart=/bin/false
|
||||
("failed", "failed"),
|
||||
)
|
||||
|
||||
# pyre-fixme[56]: Argument `not
|
||||
# eden.integration.lib.linux.is_cgroup_v2_mounted()` to decorator factory
|
||||
# `unittest.skipIf` could not be resolved in a global scope.
|
||||
@unittest.skipIf(
|
||||
not is_cgroup_v2_mounted(),
|
||||
"T36934106: Fix EdenFS systemd integration tests for cgroups v1",
|
||||
|
@ -32,12 +32,12 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
|
||||
# TODO(T33122320): Delete this test when systemd is properly integrated.
|
||||
def test_eden_start_with_systemd_disabled_does_not_say_systemd_mode_is_enabled(
|
||||
self
|
||||
self,
|
||||
) -> None:
|
||||
self.unsetenv("EDEN_EXPERIMENTAL_SYSTEMD")
|
||||
|
||||
def test(start_args: typing.List[str]) -> None:
|
||||
eden_cli: str = FindExe.EDEN_CLI # pyre-ignore[9]: T38947910
|
||||
eden_cli: str = FindExe.EDEN_CLI
|
||||
with self.subTest(start_args=start_args):
|
||||
start_process: "pexpect.spawn[str]" = pexpect.spawn(
|
||||
eden_cli,
|
||||
@ -59,14 +59,11 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
start_process.wait()
|
||||
|
||||
test(start_args=["--", "--allowRoot"])
|
||||
# pyre-ignore[6]: T38947910
|
||||
test(start_args=["--daemon-binary", FindExe.FAKE_EDENFS])
|
||||
|
||||
def test_eden_start_starts_systemd_service(self) -> None:
|
||||
subprocess.check_call(
|
||||
self.get_edenfsctl_cmd()
|
||||
# pyre-ignore[6]: T38947910
|
||||
+ ["start", "--daemon-binary", FindExe.FAKE_EDENFS]
|
||||
self.get_edenfsctl_cmd() + ["start", "--daemon-binary", FindExe.FAKE_EDENFS]
|
||||
)
|
||||
self.assert_systemd_service_is_active(eden_dir=self.eden_dir)
|
||||
|
||||
@ -74,7 +71,7 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
self.assert_systemd_service_is_stopped(eden_dir=self.eden_dir)
|
||||
subprocess.call(
|
||||
self.get_edenfsctl_cmd()
|
||||
+ [ # pyre-ignore[6]: T38947910
|
||||
+ [
|
||||
"start",
|
||||
"--daemon-binary",
|
||||
FindExe.FAKE_EDENFS,
|
||||
@ -85,7 +82,7 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
self.assert_systemd_service_is_failed(eden_dir=self.eden_dir)
|
||||
|
||||
def test_eden_start_reports_service_failure_if_edenfs_fails_during_startup(
|
||||
self
|
||||
self,
|
||||
) -> None:
|
||||
start_process = self.spawn_start_with_fake_edenfs(
|
||||
extra_args=["--", "--failDuringStartup"]
|
||||
@ -166,14 +163,13 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
return pexpect_spawn(
|
||||
FindExe.EDEN_CLI,
|
||||
self.get_required_eden_cli_args()
|
||||
# pyre-ignore[6]: T38947910
|
||||
+ ["start", "--daemon-binary", FindExe.FAKE_EDENFS] + list(extra_args),
|
||||
+ ["start", "--daemon-binary", FindExe.FAKE_EDENFS]
|
||||
+ list(extra_args),
|
||||
encoding="utf-8",
|
||||
logfile=sys.stderr,
|
||||
)
|
||||
|
||||
def get_edenfsctl_cmd(self) -> typing.List[str]:
|
||||
# pyre-ignore[6,7]: T38947910
|
||||
return [FindExe.EDEN_CLI] + self.get_required_eden_cli_args()
|
||||
|
||||
def get_required_eden_cli_args(self) -> typing.List[str]:
|
||||
@ -190,7 +186,6 @@ class SystemdTest(SystemdServiceTest, PexpectAssertionMixin):
|
||||
config_d = self.etc_eden_dir / "config.d"
|
||||
config_d.mkdir()
|
||||
with open(config_d / "systemd.toml", "w") as config_file:
|
||||
# pyre-ignore[6]: T39129461
|
||||
toml.dump(config, config_file)
|
||||
|
||||
def spoof_user_name(self, user_name: str) -> None:
|
||||
|
@ -334,11 +334,7 @@ class TakeoverTest(testcase.EdenRepoTest):
|
||||
|
||||
print("Restarting eden: %r" % (restart_cmd,))
|
||||
return pexpect.spawn(
|
||||
# pyre-ignore[6]: T38947910
|
||||
restart_cmd[0],
|
||||
restart_cmd[1:],
|
||||
logfile=sys.stdout.buffer,
|
||||
timeout=5,
|
||||
restart_cmd[0], restart_cmd[1:], logfile=sys.stdout.buffer, timeout=5
|
||||
)
|
||||
|
||||
def assert_restart_fails_with_in_progress_graceful_restart(
|
||||
|
@ -47,6 +47,8 @@ from edenscm.mercurial import (
|
||||
)
|
||||
from edenscm.mercurial.i18n import _, _n
|
||||
from edenscm.mercurial.pycompat import decodeutf8, encodeutf8, range
|
||||
|
||||
# pyre-fixme[21]: Could not find name `linelog` in `edenscmnative`.
|
||||
from edenscmnative import linelog
|
||||
|
||||
|
||||
|
@ -44,9 +44,17 @@ NoRepo = common.NoRepo
|
||||
try:
|
||||
# pyre-fixme[21]: Could not find `svn`.
|
||||
import svn
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.client`.
|
||||
import svn.client
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.core`.
|
||||
import svn.core
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.ra`.
|
||||
import svn.ra
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.delta`.
|
||||
import svn.delta
|
||||
from . import transport
|
||||
import warnings
|
||||
|
@ -23,7 +23,11 @@ from __future__ import absolute_import
|
||||
|
||||
# pyre-fixme[21]: Could not find `svn`.
|
||||
import svn.client
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.core`.
|
||||
import svn.core
|
||||
|
||||
# pyre-fixme[21]: Could not find module `svn.ra`.
|
||||
import svn.ra
|
||||
from edenscm.mercurial import util
|
||||
|
||||
|
@ -37,6 +37,7 @@ import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
# pyre-fixme[21]: Could not find name `bser` in `edenscmnative`.
|
||||
from edenscmnative import bser
|
||||
|
||||
from . import capabilities, compat, encoding, load # noqa: F401
|
||||
|
@ -31,6 +31,7 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
import ctypes
|
||||
|
||||
# pyre-fixme[21]: Could not find name `bser` in `edenscmnative`.
|
||||
from edenscmnative import bser
|
||||
|
||||
|
||||
|
@ -24,6 +24,8 @@ from edenscm.mercurial import (
|
||||
)
|
||||
from edenscm.mercurial.i18n import _
|
||||
from edenscm.mercurial.pycompat import range
|
||||
|
||||
# pyre-fixme[21]: Could not find name `linelog` in `edenscmnative`.
|
||||
from edenscmnative import linelog as linelogmod
|
||||
|
||||
from . import error as faerror, revmap as revmapmod
|
||||
|
@ -68,6 +68,7 @@ except (AttributeError, ImportError):
|
||||
pass
|
||||
|
||||
try:
|
||||
# pyre-fixme[21]: Could not find name `ignore` in `edenscm.mercurial`.
|
||||
from edenscm.mercurial import ignore
|
||||
|
||||
# pyre-fixme[16]: Module `mercurial` has no attribute `ignore`.
|
||||
|
@ -10,6 +10,7 @@ try:
|
||||
|
||||
peerapi = True
|
||||
except ImportError:
|
||||
# pyre-fixme[21]: Could not find name `peerrepository` in `edenscm.mercurial.peer`.
|
||||
from edenscm.mercurial.peer import peerrepository
|
||||
|
||||
|
||||
|
@ -488,11 +488,10 @@ def _trackdirstatesizes(lui, repo):
|
||||
dirstatesize = None
|
||||
try:
|
||||
# Eden and flat dirstate.
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `_map`.
|
||||
dirstatesize = len(dirstate._map._map)
|
||||
except AttributeError:
|
||||
# Treestate and treedirstate.
|
||||
dirstatesize = len(dirstate._map) # pyre-fixme[6]
|
||||
dirstatesize = len(dirstate._map)
|
||||
if dirstatesize is not None:
|
||||
lui.log("dirstate_size", dirstate_size=dirstatesize)
|
||||
if (
|
||||
@ -2173,7 +2172,6 @@ def unincludesubcmd(ui, repo, *pats, **opts):
|
||||
|
||||
|
||||
for c in deletesubcmd, excludesubcmd, includesubcmd, unexcludesubcmd, unincludesubcmd:
|
||||
# pyre-fixme[16]: `Optional` has no attribute `__iadd__`.
|
||||
c.__doc__ += """\n
|
||||
The effects of adding or deleting an include or exclude rule are applied
|
||||
immediately. If applying the new rule would cause a file with pending
|
||||
|
@ -211,6 +211,7 @@ from ..remotefilelog.repack import domaintenancerepack, repacklockvfs
|
||||
try:
|
||||
from itertools import zip_longest
|
||||
except ImportError:
|
||||
# pyre-fixme[21]: Could not find name `izip_longest` in `itertools`.
|
||||
from itertools import izip_longest as zip_longest
|
||||
|
||||
|
||||
|
@ -743,6 +743,8 @@ def get_winpopen4(pipei_bufsize):
|
||||
"""Same as util.popen4, but manually creates an input pipe with a
|
||||
larger than default buffer"""
|
||||
import msvcrt
|
||||
|
||||
# pyre-fixme[21]: Could not find module `_subprocess`.
|
||||
import _subprocess
|
||||
|
||||
handles = _subprocess.CreatePipe(None, pipei_bufsize)
|
||||
|
@ -490,7 +490,6 @@ class bundlerepository(localrepo.localrepository):
|
||||
|
||||
if f in self._cgfilespos:
|
||||
self._cgunpacker.seek(self._cgfilespos[f])
|
||||
# pyre-fixme[16]: Callable `changelog` has no attribute `rev`.
|
||||
linkmapper = self.changelog.rev
|
||||
return bundlefilelog(self.svfs, f, self._cgunpacker, linkmapper)
|
||||
else:
|
||||
@ -521,11 +520,9 @@ class bundlerepository(localrepo.localrepository):
|
||||
# Check if parents exist in localrepo before setting
|
||||
def setparents(self, p1, p2=nullid):
|
||||
# type: (bytes, bytes) -> None
|
||||
# pyre-fixme[16]: Callable `changelog` has no attribute `rev`.
|
||||
p1rev = self.changelog.rev(p1)
|
||||
p2rev = self.changelog.rev(p2)
|
||||
msg = _("setting parent to node %s that only exists in the bundle\n")
|
||||
# pyre-fixme[16]: Callable `changelog` has no attribute `repotiprev`.
|
||||
if self.changelog.repotiprev < p1rev:
|
||||
self.ui.warn(msg % nodemod.hex(p1))
|
||||
if self.changelog.repotiprev < p2rev:
|
||||
|
@ -15,6 +15,8 @@ from __future__ import absolute_import
|
||||
import struct
|
||||
|
||||
from ..pure.bdiff import * # noqa: F403, F401
|
||||
|
||||
# pyre-fixme[21]: Could not find name `_bdiff` in `edenscm.mercurial.cffi`.
|
||||
from . import _bdiff
|
||||
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ..pure.mpatch import * # noqa: F401, F403
|
||||
|
||||
# pyre-fixme[21]: Could not find name `_mpatch` in `edenscm.mercurial.cffi`.
|
||||
from . import _mpatch
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@ from ..pure.osutil import * # noqa: F401, F403
|
||||
|
||||
|
||||
if pycompat.isdarwin:
|
||||
# pyre-fixme[21]: Could not find name `_osutil` in `edenscm.mercurial.cffi`.
|
||||
from . import _osutil
|
||||
|
||||
# pyre-fixme[16]: Module `cffi` has no attribute `_osutil`.
|
||||
|
@ -3079,6 +3079,7 @@ def debugssl(ui, repo, source=None, **opts):
|
||||
else:
|
||||
raise error.Abort(_("only https and ssh connections are supported"))
|
||||
|
||||
# pyre-fixme[21]: Could not find name `win32` in `edenscm.mercurial.commands`.
|
||||
from . import win32
|
||||
|
||||
s = ssl.wrap_socket(
|
||||
|
@ -14,6 +14,10 @@ from ..node import bin, hex, short
|
||||
from .cmdtable import command
|
||||
|
||||
|
||||
# pyre-fixme[56]: Argument `[("t", "time-range", [],
|
||||
# edenscm.mercurial.i18n._("select time range"), edenscm.mercurial.i18n._("TIME"))]`
|
||||
# to decorator factory `edenscm.mercurial.commands.cmdtable.command` could not be
|
||||
# resolved in a global scope.
|
||||
@command("debugmetalog", [("t", "time-range", [], _("select time range"), _("TIME"))])
|
||||
def debugmetalog(ui, repo, **opts):
|
||||
# type: (...) -> None
|
||||
|
@ -354,6 +354,9 @@ class HgServer(object):
|
||||
# Return True to indicate that we should continue serving
|
||||
return True
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_MANIFEST)
|
||||
def cmd_manifest(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -391,6 +394,9 @@ class HgServer(object):
|
||||
self.debug("sending manifest for revision %r", rev_name)
|
||||
self.dump_manifest(rev_name, request)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_OLD_CAT_FILE)
|
||||
def cmd_old_cat_file(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -418,6 +424,9 @@ class HgServer(object):
|
||||
contents = self.get_file(path, rev_hash)
|
||||
self.send_chunk(request, contents)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_CAT_FILE)
|
||||
def cmd_cat_file(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -449,6 +458,9 @@ class HgServer(object):
|
||||
length_data = struct.pack(">Q", len(contents))
|
||||
self.send_chunk(request, contents, length_data)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_GET_FILE_SIZE)
|
||||
def cmd_get_file_size(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -475,6 +487,9 @@ class HgServer(object):
|
||||
data = struct.pack(">Q", size)
|
||||
self.send_chunk(request, data)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_MANIFEST_NODE_FOR_COMMIT)
|
||||
def cmd_manifest_node_for_commit(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -506,6 +521,9 @@ class HgServer(object):
|
||||
|
||||
self.send_chunk(request, node)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_FETCH_TREE)
|
||||
def cmd_fetch_tree(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -738,6 +756,9 @@ class HgServer(object):
|
||||
)
|
||||
raise ResetRepoError(e)
|
||||
|
||||
# pyre-fixme[56]: While applying decorator
|
||||
# `edenscm.mercurial.commands.eden.cmd(...)`: Expected `(Request) -> None` for 1st
|
||||
# param but got `(self: HgServer, request: Request) -> None`.
|
||||
@cmd(CMD_PREFETCH_FILES)
|
||||
def prefetch_files(self, request):
|
||||
# type: (Request) -> None
|
||||
@ -896,6 +917,23 @@ def runedenimporthelper(repo, **opts):
|
||||
return 0
|
||||
|
||||
|
||||
# pyre-fixme[56]: Argument `[("", "in-fd", "", edenscm.mercurial.i18n._("Use the
|
||||
# specified file descriptor to receive commands, rather than reading on stdin"),
|
||||
# edenscm.mercurial.i18n._("FILENO")), ("", "out-fd", "",
|
||||
# edenscm.mercurial.i18n._("Use the specified file descriptor to send command output,
|
||||
# rather than writing to stdout"), edenscm.mercurial.i18n._("FILENO")), ("",
|
||||
# "manifest", "", edenscm.mercurial.i18n._("Dump the binary manifest data for the
|
||||
# specified revision."), edenscm.mercurial.i18n._("REVISION")), ("",
|
||||
# "get-manifest-node", "", edenscm.mercurial.i18n._("Print the manifest node ID for
|
||||
# the specified revision."), edenscm.mercurial.i18n._("REVISION")), ("", "cat-file",
|
||||
# "", edenscm.mercurial.i18n._("Dump the file contents for the specified file at the
|
||||
# given file revision"), edenscm.mercurial.i18n._("PATH:REV")), ("", "get-file-size",
|
||||
# "", edenscm.mercurial.i18n._("Get the file size for the specified file at the given
|
||||
# file revision"), edenscm.mercurial.i18n._("PATH:REV")), ("", "fetch-tree", "",
|
||||
# edenscm.mercurial.i18n._("Fetch treemanifest data for the specified path at the
|
||||
# given manifest node"), edenscm.mercurial.i18n._("PATH:REV"))]` to decorator factory
|
||||
# `edenscm.mercurial.commands.cmdtable.command` could not be resolved in a global
|
||||
# scope.
|
||||
@command(
|
||||
"debugedenimporthelper",
|
||||
[
|
||||
@ -1009,6 +1047,9 @@ def eden_import_helper(ui, repo, *repo_args, **opts):
|
||||
repo.close()
|
||||
|
||||
|
||||
# pyre-fixme[56]: Argument `[]` to decorator factory
|
||||
# `edenscm.mercurial.commands.cmdtable.command` could not be resolved in a global
|
||||
# scope.
|
||||
@command("debugedenrunpostupdatehook", [])
|
||||
def edenrunpostupdatehook(ui, repo):
|
||||
# type: (ui.ui, localrepo.localrepository) -> None
|
||||
|
@ -486,7 +486,9 @@ class dirstate(object):
|
||||
if self._map.hastrackeddir(f):
|
||||
raise error.Abort(_("directory %r already in dirstate") % f)
|
||||
if os.path.isabs(f):
|
||||
raise error.Abort(_("cannot add non-root-relative path to dirstate: %s") % f)
|
||||
raise error.Abort(
|
||||
_("cannot add non-root-relative path to dirstate: %s") % f
|
||||
)
|
||||
# shadows
|
||||
for d in util.finddirs(f):
|
||||
if self._map.hastrackeddir(d):
|
||||
@ -873,9 +875,7 @@ class dirstate(object):
|
||||
cleanpaths = [] # type: List[str]
|
||||
|
||||
dmap = self._map
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `preload`.
|
||||
dmap.preload()
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `__getitem__`.
|
||||
dget = dmap.__getitem__
|
||||
madd = modified.append
|
||||
aadd = added.append
|
||||
@ -885,7 +885,6 @@ class dirstate(object):
|
||||
dadd = deleted.append
|
||||
cadd = cleanpaths.append
|
||||
ignore = self._ignore
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `copymap`.
|
||||
copymap = self._map.copymap
|
||||
|
||||
# We have seen some rare issues that a few "M" or "R" files show up
|
||||
@ -907,7 +906,6 @@ class dirstate(object):
|
||||
if t[0] == "?":
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
# pyre-fixme[19]: Expected 0 positional arguments.
|
||||
isignored = ignore(fn)
|
||||
if listignored and isignored:
|
||||
iadd(fn)
|
||||
@ -929,7 +927,6 @@ class dirstate(object):
|
||||
# iteration may change the nonnormalset as lookup states are resolved.
|
||||
if util.safehasattr(dmap, "nonnormalsetfiltered"):
|
||||
# treestate has a fast path to filter out ignored directories.
|
||||
# pyre-fixme[16]: Callable `_ignore` has no attribute `visitdir`.
|
||||
ignorevisitdir = ignore.visitdir
|
||||
|
||||
def dirfilter(path):
|
||||
@ -937,13 +934,10 @@ class dirstate(object):
|
||||
result = ignorevisitdir(path.rstrip("/"))
|
||||
return result == "all"
|
||||
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `nonnormalsetfiltered`.
|
||||
nonnormalset = dmap.nonnormalsetfiltered(dirfilter)
|
||||
else:
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `nonnormalset`.
|
||||
nonnormalset = dmap.nonnormalset
|
||||
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `otherparentset`.
|
||||
otherparentset = dmap.otherparentset
|
||||
|
||||
# The seen set is used to prevent steps 2 and 3 from processing things
|
||||
|
@ -50,13 +50,9 @@ class eden_dirstate_map(dirstate.dirstatemap):
|
||||
# never allow these to be inserted into self._map in the first place.)
|
||||
m = {
|
||||
k: (v[0], v[1], v[2])
|
||||
# pyre-fixme[16]: Callable `_map` has no attribute `items`.
|
||||
for k, v in self._map.items()
|
||||
if not (v[0] == "n" and v[2] == MERGE_STATE_NOT_APPLICABLE)
|
||||
}
|
||||
# pyre-fixme[6]: Expected `Dict[bytes, bytes]` for 4th param but got
|
||||
# `BoundMethod[typing.Callable(dirstate.dirstatemap.copymap)[[Named(self,
|
||||
# dirstate.dirstatemap)], typing.Any], eden_dirstate_map]`.
|
||||
eden_dirstate_serializer.write(st, parents, m, self.copymap)
|
||||
st.close()
|
||||
|
||||
|
@ -840,6 +840,7 @@ def _disabledhelp(path):
|
||||
def disabled():
|
||||
"""find disabled extensions from hgext. returns a dict of {name: desc}"""
|
||||
try:
|
||||
# pyre-fixme[21]: Could not find name `__index__` in `edenscm.hgext`.
|
||||
from edenscm.hgext import __index__
|
||||
|
||||
return dict(
|
||||
|
@ -483,6 +483,7 @@ class HTTPResponse(httplib.HTTPResponse):
|
||||
|
||||
return "".join(parts)
|
||||
|
||||
# pyre-fixme[14]: `readline` overrides method defined in `IOBase` inconsistently.
|
||||
def readline(self, size=-1):
|
||||
# type: (int) -> bytes
|
||||
# Fast path for a line is already available in read buffer.
|
||||
|
@ -74,11 +74,6 @@ def listdir(path, stat=False, skip=None):
|
||||
|
||||
|
||||
if not pycompat.iswindows:
|
||||
# pyre-fixme[9]: posixfile has type `Type[posixfile]`; used as `(file:
|
||||
# Union[_PathLike[Any], bytes, int, str], mode: str = ..., buffering: int = ...,
|
||||
# encoding: Optional[str] = ..., errors: Optional[str] = ..., newline:
|
||||
# Optional[str] = ..., closefd: bool = ..., opener: Optional[(str, int) -> int] =
|
||||
# ...) -> IO[Any]`.
|
||||
posixfile = open
|
||||
|
||||
_SCM_RIGHTS = 0x01
|
||||
@ -114,6 +109,8 @@ if not pycompat.iswindows:
|
||||
(u"cmsg_len", _cmsg_len_t),
|
||||
(u"cmsg_level", ctypes.c_int),
|
||||
(u"cmsg_type", ctypes.c_int),
|
||||
# pyre-fixme[6]: `*` is not supported for operand types
|
||||
# `Type[ctypes.c_ubyte]` and `int`.
|
||||
(u"cmsg_data", ctypes.c_ubyte * 0),
|
||||
]
|
||||
|
||||
|
@ -1807,7 +1807,6 @@ class revlog(object):
|
||||
if not data:
|
||||
return b"", data
|
||||
|
||||
# pyre-fixme[16]: Callable `_compressor` has no attribute `compress`.
|
||||
compressed = self._compressor.compress(data)
|
||||
|
||||
if compressed:
|
||||
|
@ -1895,6 +1895,8 @@ class atomictempfile(BinaryIO):
|
||||
# type: () -> atomictempfile
|
||||
return self
|
||||
|
||||
# pyre-fixme[14]: `__exit__` overrides method defined in `IO` inconsistently.
|
||||
# pyre-fixme[14]: `__exit__` overrides method defined in `IO` inconsistently.
|
||||
def __exit__(
|
||||
self,
|
||||
exctype, # type: Optional[Type[BaseException]]
|
||||
@ -1970,6 +1972,7 @@ class atomictempfile(BinaryIO):
|
||||
# type: () -> bool
|
||||
return self._fp.writable()
|
||||
|
||||
# pyre-fixme[15]: `write` overrides method defined in `IO` inconsistently.
|
||||
def write(self, s):
|
||||
# type: (bytes) -> None
|
||||
return self._fp.write(s)
|
||||
|
Loading…
Reference in New Issue
Block a user