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

docs: Added fix for building on Windows and resolved other build errors #2395

Merged
merged 7 commits into from
Mar 12, 2023

Conversation

munsterlander
Copy link
Contributor

@munsterlander munsterlander commented Mar 11, 2023

Description

@eukleshnin identified issues with building the documents locally on a Windows workstation. Namely, the following was occurring:

reading sources... [  2%] flame/layout/align_component
Exception occurred:

FileNotFoundError: [WinError 2]

This was determined to be a combination of several things.

  1. In dart_domain.py, the subprocess.run calling dartdoc_json did not have the shell parameter set to true. This solves the error about the file not being found. This then generates errors that subsequent references to the temp_file did not exist.
  2. This was due to the default setting with Python tempfile where when it determines the temp file has been closed, it deletes it; however, it was still needed, so by setting the delete=False parameter, the file would still remain.
  3. Unfortunately, because it still remains, it needs to be deleted once it is no longer needed. Trying to use finally: with the try block failed to produce the results desired, so the temp file name was registered with the class so it can be deleted in the original calling function if it exists. This proved successful.
  4. Although not critical, the same temp file uses a suffix of json so it was creating files xxxxjson. By adding the "." in the suffix, it creates valid file names now. This doesn't actually fix anything, it just seemed wrong, so I fixed it to be valid files if ever needed down the road.

Now that the docs built, there were several warnings that could be resolved:

  1. Overlays.md was not referenced in the TOC tree.
  2. Since overlays were removed in docs: Break out overlays docs #2384, the Platformer tutorial had a link to the old path and it needed to be updated.

Finally and open for discussion, during this process of debugging, I upgraded all packages to the most current to see what impacts there were. The following is the old and new potential requirements.txt:

----Old
linkify-it-py==2.0.0
myst-parser==0.18.1
Pygments==2.12.0
Sphinx==5.0.2
sphinxcontrib-mermaid==0.8.1
sphinx-autobuild==2021.3.14
jinja2==3.1.2

----- New
linkify-it-py==2.0.0
myst-parser==1.0.0
Pygments==2.14.0
Sphinx==6.1.3
sphinxcontrib-mermaid==0.8.1
sphinx-autobuild==2021.3.14
Jinja2==3.1.2

The only byproduct of this upgrade was a deprecated package warning for attrs_image in conf.py which was updated to attrs_inline. I made that change initially for this PR, but backed it out as I didn't know if there was a desire to update the requirements.txt and felt some discussion may be warranted.

Regardless, with everything upgraded or left as is, the other fixes resolve the issues on Windows.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Copy link
Contributor

@st-pasha st-pasha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like temp_file is only really needed on lines 176-183, and can be deleted afterwards. Which means you can add a finally block for try: on line 174, and have the file deleted within that block -- then the file name can remain a local variable within _scan_source_file(), and doesn't need to become an instance member.

@munsterlander
Copy link
Contributor Author

munsterlander commented Mar 12, 2023

It looks like temp_file is only really needed on lines 176-183, and can be deleted afterwards. Which means you can add a finally block for try: on line 174, and have the file deleted within that block -- then the file name can remain a local variable within _scan_source_file(), and doesn't need to become an instance member.

That is what I tried initially, hence why I called it out in the PR description (3). When you do the finally and delete of the file, you get an error in the JSON decoder. I presume because when you call delete, it then deletes the JSON from memory which is then being set in the parent function. I presume that when finally gets called it overwrites the return and thus you have the issue. It likely works for Unix but not on windows - not certain.

A fairly comprehensive explanation is here: https://stackoverflow.com/questions/19805654/python-try-finally-block-returns.

@st-pasha
Copy link
Contributor

st-pasha commented Mar 12, 2023

Here's the docs for tempfile.NamedTemporaryFile:

This function operates exactly as TemporaryFile() does, except that the file is guaranteed to have a visible name in the file system (on Unix, the directory entry is not unlinked). That name can be retrieved from the .name attribute of the returned file-like object. Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows). If delete is true (the default), the file is deleted as soon as it is closed.

So, I think the best approach would be to:

  • add delete=False parameter to NamedTemporaryFile constructor;
  • close the file immediately (as first statement inside with);
  • delete the file within the finally: block.

@munsterlander
Copy link
Contributor Author

munsterlander commented Mar 12, 2023

Ok, so these commands are not executing in the way I would think they should:

# In try:
temp_file.close # no error occurs
temp_file.close() # no error occurs

# In finally:
temp_file.delete # does not delete the file
temp_file.delete() # throws TypeError: 'bool' object is not callable

I have tried all combinations and yeah, no go. My Python is not the strongest here.

Full Code:

def _scan_source_file(self):
        with tempfile.NamedTemporaryFile(mode='rt', suffix='.json', delete=False) as temp_file:
            try:
                #self.temp_file_path = temp_file.name
                temp_file.close
                subprocess.run(
                    ['dartdoc_json', self.source_file, '--output', temp_file.name],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    check=True,
                    shell=True,
                )
                json_string = temp_file.read()
                return self._extract_symbol(json_string)
            except subprocess.CalledProcessError as e:
                cmd = ' '.join(e.cmd)
                raise RuntimeError(
                    f'Command `{cmd}` returned with exit status'
                    f' {e.returncode}\n{e.output.decode("utf-8")}'
                )
            finally:
                temp_file.delete

@st-pasha
Copy link
Contributor

Right, so:

  • temp_file.close is just a function object, you need () to call this method.
  • temp_file.delete is the "delete" property passed in the constructor, so this is just a boolean value False. This cannot be called (there is no such method as temp_file.delete()).
  • in order to delete a file you can say os.remove(temp_file.name).

@munsterlander
Copy link
Contributor Author

munsterlander commented Mar 12, 2023

Make sense. Updated and ran:

  File "C:\Python39\lib\tempfile.py", line 474, in func_wrapper
    return func(*args, **kwargs)
ValueError: I/O operation on closed file.

Eliminating the close gets you:

  File "C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\dart_domain.py", line 193, in _scan_source_file
    os.remove(temp_file.name)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\munsterlander\\AppData\\Local\\Temp\\tmpe87i6lol.json'

These are the errors that I was getting earlier today when I was solving it and which led to just putting the name in the class.

@st-pasha
Copy link
Contributor

Ah, I guess reading the file now needs an explicit open:

with open(temp_file.name, 'r') as t:
    json_string = t.read()

@munsterlander
Copy link
Contributor Author

munsterlander commented Mar 12, 2023

So let me just make sure I have this straight, create a temp file, then immediately close it, pipe data to it, reopen it to read it to put the contents into a var to be returned, and on exit delete the temp file.

As crazy as that seems, it worked.

Edit: Just went back and confirmed it was deleting the files from the temp directory.

@st-pasha
Copy link
Contributor

Yes, that's correct:

  1. Create a temporary file and automatically open it.
  2. Realize that we don't need it open (because Windows), so close the file.
  3. Let an external script dartdoc_json write into the file.
  4. NOW we can open the file again and read its contents.
  5. Parse the file contents and return the result.
  6. Oh, and the finally block ensures that the file gets deleted in the end, regardless of whether we return normally or there was an exception.

@munsterlander
Copy link
Contributor Author

Crazy and I had tried numerous variances, but didn't think to close and read back to memory. Anyway, all the code has been updated and I appreciate the help!

I don't know if you saw my comments on your already merged PR for melos doc-setup, but it's a similar windows issue thing. The command is there and working, just needs some tweaks to pass the formatter.

@munsterlander
Copy link
Contributor Author

munsterlander commented Mar 12, 2023

Just added another fix for melos doc-serve. @eukleshnin observed that while doc-build worked, doc-serve still generated 404 errors. Looking into it, doc-serve was building the docs under the build directory and not under build\html as expected. The make.bat and Makefile had different paths, so this resolves that.

Sphinx docs discuss the defaults for autobuild: https://pypi.org/project/sphinx-autobuild/
and for regular build here: https://www.sphinx-doc.org/en/master/man/sphinx-build.html.

Additionally, I have added the flags that currently exist under the Makefile so they both match.

@spydon spydon enabled auto-merge (squash) March 12, 2023 20:44
@spydon spydon merged commit 63704c4 into flame-engine:main Mar 12, 2023
@munsterlander munsterlander deleted the DartDocWindows branch March 12, 2023 20:57
eukleshnin pushed a commit to eukleshnin/flame that referenced this pull request Mar 12, 2023
…rs (flame-engine#2395)

<!--
The title of your PR on the line above should start with a [Conventional
Commit] prefix
(`fix:`, `feat:`, `docs:`, `test:`, `chore:`, `refactor:`, `perf:`,
`build:`, `ci:`,
`style:`, `revert:`). This title will later become an entry in the
[CHANGELOG], so please
make sure that it summarizes the PR adequately.
-->

# Description
<!--
Provide a description of what this PR is doing.
If you're modifying existing behavior, describe the existing behavior,
how this PR is changing it,
and what motivated the change. If this is a breaking change, specify
explicitly which APIs were
changed.
-->

@eukleshnin identified issues with building the documents locally on a
Windows workstation. Namely, the following was occurring:

```
reading sources... [  2%] flame/layout/align_component
Exception occurred:

FileNotFoundError: [WinError 2]
```
This was determined to be a combination of several things.

1. In `dart_domain.py`, the `subprocess.run` calling dartdoc_json did
not have the shell parameter set to true. This solves the error about
the file not being found. This then generates errors that subsequent
references to the `temp_file` did not exist.
2. This was due to the default setting with Python `tempfile` where when
it determines the temp file has been closed, it deletes it; however, it
was still needed, so by setting the `delete=False` parameter, the file
would still remain.
3. Unfortunately, because it still remains, it needs to be deleted once
it is no longer needed. Trying to use `finally:` with the `try` block
failed to produce the results desired, so the temp file name was
registered with the class so it can be deleted in the original calling
function if it exists. This proved successful.
4. Although not critical, the same temp file uses a suffix of `json` so
it was creating files `xxxxjson`. By adding the "." in the suffix, it
creates valid file names now. This doesn't actually fix anything, it
just seemed wrong, so I fixed it to be valid files if ever needed down
the road.

Now that the docs built, there were several warnings that could be
resolved:
1. `Overlays.md` was not referenced in the TOC tree.
2. Since overlays were removed in
flame-engine#2384, the Platformer tutorial
had a link to the old path and it needed to be updated.

Finally and open for discussion, during this process of debugging, I
upgraded all packages to the most current to see what impacts there
were. The following is the old and new potential `requirements.txt`:

```
----Old
linkify-it-py==2.0.0
myst-parser==0.18.1
Pygments==2.12.0
Sphinx==5.0.2
sphinxcontrib-mermaid==0.8.1
sphinx-autobuild==2021.3.14
jinja2==3.1.2

----- New
linkify-it-py==2.0.0
myst-parser==1.0.0
Pygments==2.14.0
Sphinx==6.1.3
sphinxcontrib-mermaid==0.8.1
sphinx-autobuild==2021.3.14
Jinja2==3.1.2
```

The only byproduct of this upgrade was a deprecated package warning for
`attrs_image` in `conf.py` which was updated to `attrs_inline`. I made
that change initially for this PR, but backed it out as I didn't know if
there was a desire to update the `requirements.txt` and felt some
discussion may be warranted.

Regardless, with everything upgraded or left as is, the other fixes
resolve the issues on Windows.

## Checklist
<!--
Before you create this PR confirm that it meets all requirements listed
below by checking the
relevant checkboxes with `[x]`. If some checkbox is not applicable, mark
it as `[-]`.
-->

- [X] I have followed the [Contributor Guide] when preparing my PR.
- [ ] I have updated/added tests for ALL new/updated/fixed
functionality.
- [X] I have updated/added relevant documentation in `docs` and added
dartdoc comments with `///`.
- [ ] I have updated/added relevant examples in `examples` or `docs`.


## Breaking Change?
<!--
Would your PR require Flame users to update their apps following your
change?

If yes, then the title of the PR should include "!" (for example,
`feat!:`, `fix!:`). See
[Conventional Commit] for details. Also, for a breaking PR uncomment and
fill in the "Migration
instructions" section below.

### Migration instructions

If the PR is breaking, uncomment this header and add instructions for
how to migrate from the
currently released version to the new proposed way.
-->

- [ ] Yes, this PR is a breaking change.
- [X] No, this PR is not a breaking change.


## Related Issues
<!--
Indicate which issues this PR resolves, if any. For example:

Closes flame-engine#1234
!-->

<!-- Links -->
[Contributor Guide]:
https://github.com/flame-engine/flame/blob/main/CONTRIBUTING.md
[Conventional Commit]: https://conventionalcommits.org
[CHANGELOG]:
https://github.com/flame-engine/flame/blob/main/CHANGELOG.md

---------

Co-authored-by: Lukas Klingsbo <me@lukas.fyi>
spydon pushed a commit that referenced this pull request Mar 13, 2023
As mentioned in #2395, there are several lingering issues that can be solved by bumping the Python package requirements up. All packages need / can run on Python 3.8+, so the docs do not need to be updated regarding that.

This PR specifically fixes the following warnings:

flame\doc\bridge_packages\bridge_packages.md:4: WARNING: 'myst' reference target not found: ..\bridge_packages\flame_audio\flame_audio.md
....There were a lot of those....

`attrs_image` is deprecated.  Use `attrs_inline` instead.

Additionally, this PR adds a new command melos doc-kill which executes the kill-server.py script. This script relies on psutil which has been added to the requirements.txt, but allows a cross platform ability to isolate and kill process threads that are locking the port 8000. This commonly happens when you exit melos doc-serve and the internal web server doesn't die as well. This may not be an issue for Unix platforms, but for Windows users its extremely annoying.

The only alternative for Windows users is to manually do:

netstat -ano | findstr :8000
// Then run:
taskkill /PID XXXXX /F 

As I mentioned in the other PR, I split this out so it can be debated mainly for the bump in requirements; however, I feel the benefits are very worth it. I marked this as breaking just because it changes the base package requirements and adds a package which may not qualify as breaking, depending on how you look at it.

Edit: Forgot that this PR also fixes a typo in the melos doc-serve description and corrects the misspelling of everytime to every time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants